생성: {report.get('generated_at', '')} |
{report.get('symbol', '')} |
diff --git a/src/deepcoin/evaluation/signal_type_report.py b/src/bithumb/evaluation/signal_type_report.py
similarity index 97%
rename from src/deepcoin/evaluation/signal_type_report.py
rename to src/bithumb/evaluation/signal_type_report.py
index 2211008..45fcfb6 100644
--- a/src/deepcoin/evaluation/signal_type_report.py
+++ b/src/bithumb/evaluation/signal_type_report.py
@@ -7,12 +7,12 @@ from datetime import datetime
from pathlib import Path
from typing import Any
-from deepcoin.evaluation.gt_align import (
+from bithumb.evaluation.gt_align import (
SIGNAL_TYPE_LABELS,
SIGNAL_TYPE_PRIMARY_TECHNIQUES,
summarize_signal_type_matrix,
)
-from deepcoin.techniques.base import TechniqueResult
+from bithumb.techniques.base import TechniqueResult
def build_signal_type_report(
@@ -163,7 +163,7 @@ def render_signal_type_html(report: dict[str, Any], html_path: Path) -> Path:
생성: {report.get('generated_at', '')} |
{report.get('symbol', '')} |
diff --git a/src/deepcoin/ground_truth/__init__.py b/src/bithumb/ground_truth/__init__.py
similarity index 100%
rename from src/deepcoin/ground_truth/__init__.py
rename to src/bithumb/ground_truth/__init__.py
diff --git a/src/deepcoin/ground_truth/breakout.py b/src/bithumb/ground_truth/breakout.py
similarity index 99%
rename from src/deepcoin/ground_truth/breakout.py
rename to src/bithumb/ground_truth/breakout.py
index 02046df..969b902 100644
--- a/src/deepcoin/ground_truth/breakout.py
+++ b/src/bithumb/ground_truth/breakout.py
@@ -7,7 +7,7 @@ from typing import Protocol
import pandas as pd
-from deepcoin.ground_truth.zigzag import Pivot
+from bithumb.ground_truth.zigzag import Pivot
class _LegLike(Protocol):
diff --git a/src/deepcoin/ground_truth/chart.py b/src/bithumb/ground_truth/chart.py
similarity index 99%
rename from src/deepcoin/ground_truth/chart.py
rename to src/bithumb/ground_truth/chart.py
index 85ba77a..e6ebd4d 100644
--- a/src/deepcoin/ground_truth/chart.py
+++ b/src/bithumb/ground_truth/chart.py
@@ -9,7 +9,7 @@ from typing import Any
import pandas as pd
-from deepcoin.data.candle_loader import load_candles
+from bithumb.data.candle_loader import load_candles
# 0이면 제한 없이 전체 봉 표시
DEFAULT_MAX_CANDLES = 0
@@ -254,7 +254,7 @@ _HTML_TEMPLATE = """
-
DeepCoin Chart
+
Bithumb Chart
@@ -284,7 +284,7 @@ __EXTRA_STYLES__
- DeepCoin Chart
+ Bithumb Chart
__EXTRA_BODY__
diff --git a/src/deepcoin/ground_truth/divergence.py b/src/bithumb/ground_truth/divergence.py
similarity index 99%
rename from src/deepcoin/ground_truth/divergence.py
rename to src/bithumb/ground_truth/divergence.py
index f170057..8e32438 100644
--- a/src/deepcoin/ground_truth/divergence.py
+++ b/src/bithumb/ground_truth/divergence.py
@@ -6,7 +6,7 @@ from dataclasses import dataclass
import pandas as pd
-from deepcoin.techniques.indicators import macd, rsi
+from bithumb.techniques.indicators import macd, rsi
@dataclass(frozen=True)
diff --git a/src/deepcoin/ground_truth/ground_truth.py b/src/bithumb/ground_truth/ground_truth.py
similarity index 96%
rename from src/deepcoin/ground_truth/ground_truth.py
rename to src/bithumb/ground_truth/ground_truth.py
index f9634c7..4ee3cb6 100644
--- a/src/deepcoin/ground_truth/ground_truth.py
+++ b/src/bithumb/ground_truth/ground_truth.py
@@ -10,13 +10,13 @@ from typing import Any
import pandas as pd
-from deepcoin.data.candle_loader import load_candles
-from deepcoin.data.intervals import interval_label
-from deepcoin.ground_truth.pnl import simulate_gt_pnl
-from deepcoin.ground_truth.breakout import find_breakout_buy_pivots
-from deepcoin.ground_truth.divergence import find_divergence_signals
-from deepcoin.ground_truth.pullback import find_pullback_buy_pivots
-from deepcoin.ground_truth.zigzag import Pivot, find_zigzag_pivots
+from bithumb.data.candle_loader import load_candles
+from bithumb.data.intervals import interval_label
+from bithumb.ground_truth.pnl import simulate_gt_pnl
+from bithumb.ground_truth.breakout import find_breakout_buy_pivots
+from bithumb.ground_truth.divergence import find_divergence_signals
+from bithumb.ground_truth.pullback import find_pullback_buy_pivots
+from bithumb.ground_truth.zigzag import Pivot, find_zigzag_pivots
@dataclass(frozen=True)
diff --git a/src/bithumb/ground_truth/order_sizing.py b/src/bithumb/ground_truth/order_sizing.py
new file mode 100644
index 0000000..8de0de9
--- /dev/null
+++ b/src/bithumb/ground_truth/order_sizing.py
@@ -0,0 +1,122 @@
+"""총평가금액 구간별 매수(현금) 상한."""
+
+from __future__ import annotations
+
+from typing import Any
+
+# 총평가금액(원) 구간 — 높은 구간이 우선 적용
+EQUITY_TIER_100M = 100_000_000
+EQUITY_TIER_1B = 1_000_000_000
+EQUITY_TIER_10B = 10_000_000_000
+
+BUY_SIZING_RULE_LABEL = "총평가 1억↑ 현금 10% · 10억↑ 5% · 100억↑ 1%"
+BASE_BUY_CASH_PCT = 0.10
+BASE_SELL_COIN_PCT = 0.10
+
+
+def effective_buy_cash_pct(
+ equity_krw: float,
+ base_pct: float | None = BASE_BUY_CASH_PCT,
+) -> float:
+ """1회 매수 허용 현금 비율 (구간 tier + 소액 base).
+
+ 1억 미만은 ``base_pct``(기본 10%)로 분할 매수한다.
+ 1억 이상은 기존 tier(10%/5%/1%)가 적용된다.
+ """
+ tier_pct = buy_cash_pct(equity_krw)
+ if tier_pct is not None:
+ return tier_pct
+ if base_pct is not None:
+ return max(0.0, min(float(base_pct), 1.0))
+ return 1.0
+
+
+def effective_sell_coin_pct(sell_pct: float | None = BASE_SELL_COIN_PCT) -> float:
+ """1회 매도 허용 코인 비율 (기본 10%)."""
+ if sell_pct is None:
+ return 1.0
+ return max(0.0, min(float(sell_pct), 1.0))
+
+
+def buy_cash_pct(equity_krw: float) -> float | None:
+ """총평가금액에 따른 1회 매수 허용 현금 비율.
+
+ Args:
+ equity_krw: 현재 총평가금액(원).
+
+ Returns:
+ 허용 비율(0~1). 1억 미만이면 None(비율 상한 없음).
+ """
+ if equity_krw >= EQUITY_TIER_10B:
+ return 0.01
+ if equity_krw >= EQUITY_TIER_1B:
+ return 0.05
+ if equity_krw >= EQUITY_TIER_100M:
+ return 0.10
+ return None
+
+
+def max_buy_from_cash(
+ equity_krw: float,
+ cash_krw: float,
+ *,
+ base_pct: float | None = BASE_BUY_CASH_PCT,
+) -> float:
+ """구간별·base 규칙을 반영한 1회 매수 최대 금액.
+
+ Args:
+ equity_krw: 현재 총평가금액(원).
+ cash_krw: 보유 현금(원).
+ base_pct: 1억 미만 계좌 1회 매수 비율 (None이면 전액).
+
+ Returns:
+ 매수에 사용 가능한 최대 원화.
+ """
+ cash = max(float(cash_krw), 0.0)
+ pct = effective_buy_cash_pct(equity_krw, base_pct)
+ return cash * pct
+
+
+def max_sell_coin_qty(
+ coin_qty: float,
+ *,
+ sell_pct: float | None = BASE_SELL_COIN_PCT,
+) -> float:
+ """1회 매도 최대 코인 수량 (보유 비율 cap + 클러스터 분할 전).
+
+ Args:
+ coin_qty: 보유 코인 수량.
+ sell_pct: 1회 매도 허용 비율 (None이면 전량).
+
+ Returns:
+ 이번 매도 사이클에서 사용 가능한 최대 수량.
+ """
+ qty = max(float(coin_qty), 0.0)
+ pct = effective_sell_coin_pct(sell_pct)
+ return qty * pct
+
+
+def buy_sizing_metadata(
+ *,
+ base_buy_pct: float | None = BASE_BUY_CASH_PCT,
+ base_sell_pct: float | None = BASE_SELL_COIN_PCT,
+) -> dict[str, Any]:
+ """시뮬 결과·차트에 포함할 매수·매도 상한 메타."""
+ base_label = (
+ f" · 1억 미만 현금 {base_buy_pct * 100:.0f}%"
+ if base_buy_pct is not None
+ else ""
+ )
+ return {
+ "buy_sizing_rule": BUY_SIZING_RULE_LABEL + base_label,
+ "sell_sizing_rule": f"보유 코인 {base_sell_pct * 100:.0f}% 분할 매도"
+ if base_sell_pct is not None
+ else "전량 매도",
+ "base_buy_cash_pct": base_buy_pct,
+ "base_sell_coin_pct": base_sell_pct,
+ "buy_sizing_tiers": [
+ {"min_equity_krw": EQUITY_TIER_100M, "max_cash_pct": 0.10},
+ {"min_equity_krw": EQUITY_TIER_1B, "max_cash_pct": 0.05},
+ {"min_equity_krw": EQUITY_TIER_10B, "max_cash_pct": 0.01},
+ ],
+ }
diff --git a/src/deepcoin/ground_truth/pnl.py b/src/bithumb/ground_truth/pnl.py
similarity index 90%
rename from src/deepcoin/ground_truth/pnl.py
rename to src/bithumb/ground_truth/pnl.py
index 468a03a..930c9c2 100644
--- a/src/deepcoin/ground_truth/pnl.py
+++ b/src/bithumb/ground_truth/pnl.py
@@ -6,7 +6,14 @@ from dataclasses import asdict, dataclass
from datetime import datetime, timedelta
from typing import Any
-from deepcoin.ground_truth.order_sizing import buy_sizing_metadata, max_buy_from_cash
+from bithumb.ground_truth.order_sizing import (
+ BASE_BUY_CASH_PCT,
+ BASE_SELL_COIN_PCT,
+ buy_sizing_metadata,
+ max_buy_from_cash,
+ max_sell_coin_qty,
+)
+from bithumb.ground_truth.sizing_rules import resolve_buy_cash_pct, resolve_sell_coin_pct
def _fill_price(signal_price: float, side: str, slippage_rate: float) -> float:
@@ -177,6 +184,9 @@ def simulate_gt_signals_pnl(
min_order_krw: float = 5_000.0,
slippage_rate: float = 0.0,
daily_max_trades: int | None = None,
+ buy_cash_pct: float | None = BASE_BUY_CASH_PCT,
+ sell_coin_pct: float | None = BASE_SELL_COIN_PCT,
+ sizing_rules: dict[str, Any] | None = None,
sim_lookback_days: int = 365,
data_end: str | None = None,
last_mark_price: float | None = None,
@@ -186,7 +196,8 @@ def simulate_gt_signals_pnl(
- 시뮬 기간: data_end 기준 최근 sim_lookback_days
- 연속 매수: 가용 원화를 매수 신호 수로 균등 분할
- 총평가 1억↑ 현금 10% · 10억↑ 5% · 100억↑ 1% 상한
- - 연속 매도: 보유 코인을 매도 신호 수로 균등 분할 (상한 없음)
+ - 1억 미만: buy_cash_pct(기본 10%) 분할 매수 · sell_coin_pct(기본 10%) 분할 매도
+ - 연속 매도: 허용 수량을 매도 신호 수로 균등 분할
- 원화 부족 시 매수 스킵, 코인 없으면 매도 스킵
Args:
@@ -251,7 +262,12 @@ def simulate_gt_signals_pnl(
for side, cluster in _cluster_signals(period_signals):
cluster_size = len(cluster)
if side == "buy":
- budget = cash
+ effective_buy_pct = resolve_buy_cash_pct(
+ sizing_rules, cluster_size, buy_cash_pct
+ )
+ equity = cash + coin_qty * float(cluster[0]["price"])
+ cash_cap = max_buy_from_cash(equity, cash, base_pct=effective_buy_pct)
+ budget = min(cash, cash_cap)
per_buy = budget / cluster_size if cluster_size else 0.0
for sig in cluster:
trade_id += 1
@@ -282,7 +298,7 @@ def simulate_gt_signals_pnl(
)
continue
equity = cash + coin_qty * price
- cash_cap = max_buy_from_cash(equity, cash)
+ cash_cap = max_buy_from_cash(equity, cash, base_pct=effective_buy_pct)
order_krw = min(per_buy, cash, cash_cap)
if order_krw < min_order_krw:
@@ -335,7 +351,10 @@ def simulate_gt_signals_pnl(
)
)
else:
- budget_coin = coin_qty
+ effective_sell_pct = resolve_sell_coin_pct(
+ sizing_rules, cluster_size, sell_coin_pct
+ )
+ budget_coin = max_sell_coin_qty(coin_qty, sell_pct=effective_sell_pct)
per_sell = budget_coin / cluster_size if cluster_size else 0.0
for sig in cluster:
trade_id += 1
@@ -436,7 +455,15 @@ def simulate_gt_signals_pnl(
"fee_rate": fee_rate,
"slippage_rate": slippage_rate,
"daily_max_trades": daily_max_trades,
- **buy_sizing_metadata(),
+ **buy_sizing_metadata(base_buy_pct=buy_cash_pct, base_sell_pct=sell_coin_pct),
+ "sizing_rules_applied": sizing_rules is not None,
+ "learned_default_buy_cash_pct": (
+ sizing_rules.get("default_buy_cash_pct") if sizing_rules else None
+ ),
+ "learned_default_sell_coin_pct": (
+ sizing_rules.get("default_sell_coin_pct") if sizing_rules else None
+ ),
+ "learned_by_cluster": sizing_rules.get("by_cluster") if sizing_rules else None,
"sim_lookback_days": sim_lookback_days,
"period_from": start_str,
"period_to": end_dt.strftime("%Y-%m-%d %H:%M:%S"),
@@ -468,7 +495,7 @@ def _empty_signal_pnl(
"total_pnl_krw": 0.0,
"total_return_pct": 0.0,
"fee_rate": fee_rate,
- **buy_sizing_metadata(),
+ **buy_sizing_metadata(base_buy_pct=buy_cash_pct, base_sell_pct=sell_coin_pct),
"sim_lookback_days": sim_lookback_days,
"period_from": period_from,
"period_to": period_to,
diff --git a/src/deepcoin/ground_truth/pullback.py b/src/bithumb/ground_truth/pullback.py
similarity index 98%
rename from src/deepcoin/ground_truth/pullback.py
rename to src/bithumb/ground_truth/pullback.py
index e85a5c6..2eeb7c6 100644
--- a/src/deepcoin/ground_truth/pullback.py
+++ b/src/bithumb/ground_truth/pullback.py
@@ -6,7 +6,7 @@ import pandas as pd
from typing import Protocol
-from deepcoin.ground_truth.zigzag import Pivot
+from bithumb.ground_truth.zigzag import Pivot
class _LegLike(Protocol):
diff --git a/src/bithumb/ground_truth/sizing_rules.py b/src/bithumb/ground_truth/sizing_rules.py
new file mode 100644
index 0000000..91ea6d2
--- /dev/null
+++ b/src/bithumb/ground_truth/sizing_rules.py
@@ -0,0 +1,97 @@
+"""연속 매수·매도 클러스터 상태별 매수·매도 비율 규칙."""
+
+from __future__ import annotations
+
+import json
+from datetime import datetime
+from pathlib import Path
+from typing import Any
+
+CLUSTER_SIZE_KEYS: tuple[str, ...] = ("1", "2", "3+")
+
+
+def cluster_size_key(cluster_size: int) -> str:
+ """클러스터 크기를 규칙 lookup 키로 변환한다."""
+ size = max(int(cluster_size), 1)
+ if size >= 3:
+ return "3+"
+ return str(size)
+
+
+def resolve_buy_cash_pct(
+ rules: dict[str, Any] | None,
+ cluster_size: int,
+ default: float | None,
+) -> float | None:
+ """클러스터 상태에 맞는 1회 매수 현금 비율을 반환한다."""
+ if not rules:
+ return default
+ by_side = (rules.get("by_cluster") or {}).get("buy") or {}
+ key = cluster_size_key(cluster_size)
+ if key in by_side:
+ return float(by_side[key])
+ fallback = rules.get("default_buy_cash_pct")
+ if fallback is not None:
+ return float(fallback)
+ return default
+
+
+def resolve_sell_coin_pct(
+ rules: dict[str, Any] | None,
+ cluster_size: int,
+ default: float | None,
+) -> float | None:
+ """클러스터 상태에 맞는 1회 매도 코인 비율을 반환한다."""
+ if not rules:
+ return default
+ by_side = (rules.get("by_cluster") or {}).get("sell") or {}
+ key = cluster_size_key(cluster_size)
+ if key in by_side:
+ return float(by_side[key])
+ fallback = rules.get("default_sell_coin_pct")
+ if fallback is not None:
+ return float(fallback)
+ return default
+
+
+def load_sizing_rules(path: Path) -> dict[str, Any] | None:
+ """JSON 사이징 규칙을 로드한다. 파일이 없으면 None."""
+ if not path.exists():
+ return None
+ with path.open(encoding="utf-8") as fp:
+ data = json.load(fp)
+ return data if isinstance(data, dict) else None
+
+
+def save_sizing_rules(rules: dict[str, Any], path: Path) -> Path:
+ """사이징 규칙 JSON 저장."""
+ path.parent.mkdir(parents=True, exist_ok=True)
+ with path.open("w", encoding="utf-8") as fp:
+ json.dump(rules, fp, ensure_ascii=False, indent=2)
+ return path
+
+
+def empty_by_cluster() -> dict[str, dict[str, float]]:
+ """빈 by_cluster 구조."""
+ return {"buy": {}, "sell": {}}
+
+
+def merge_rules(
+ *,
+ default_buy: float,
+ default_sell: float,
+ by_cluster: dict[str, dict[str, float]] | None = None,
+ technique_id: str = "",
+ symbol: str = "",
+ tuning: dict[str, Any] | None = None,
+) -> dict[str, Any]:
+ """튜닝 결과 dict를 표준 규칙 형식으로 만든다."""
+ return {
+ "generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+ "technique_id": technique_id,
+ "symbol": symbol,
+ "default_buy_cash_pct": round(float(default_buy), 4),
+ "default_sell_coin_pct": round(float(default_sell), 4),
+ "by_cluster": by_cluster or empty_by_cluster(),
+ "tuning": tuning or {},
+ }
diff --git a/src/bithumb/ground_truth/sizing_tune.py b/src/bithumb/ground_truth/sizing_tune.py
new file mode 100644
index 0000000..ce214bd
--- /dev/null
+++ b/src/bithumb/ground_truth/sizing_tune.py
@@ -0,0 +1,273 @@
+"""연속 매수·매도 클러스터별 사이징 비율 튜닝."""
+
+from __future__ import annotations
+
+from datetime import datetime, timedelta
+from typing import Any
+
+from bithumb.config import Settings
+from bithumb.evaluation.causal_sim import normalize_signals_for_sim
+from bithumb.ground_truth.pnl import _cluster_signals, simulate_gt_signals_pnl
+from bithumb.ground_truth.sizing_rules import (
+ CLUSTER_SIZE_KEYS,
+ cluster_size_key,
+ merge_rules,
+)
+
+PCT_CANDIDATES: tuple[float, ...] = (
+ 0.10,
+ 0.15,
+ 0.20,
+ 0.25,
+ 0.30,
+ 0.40,
+ 0.50,
+ 0.60,
+ 0.70,
+ 0.80,
+ 1.0,
+)
+
+
+def _parse_signal_dt(value: str) -> datetime:
+ return datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
+
+
+def _sim_kwargs(settings: Settings, data_end: str, last_mark_price: float) -> dict[str, Any]:
+ return {
+ "initial_cash_krw": settings.gt_initial_cash_krw,
+ "fee_rate": settings.gt_trading_fee_rate,
+ "min_order_krw": settings.ops_min_order_krw,
+ "slippage_rate": settings.ops_slippage_rate,
+ "daily_max_trades": settings.ops_daily_max_trades,
+ "sim_lookback_days": settings.gt_sim_lookback_days,
+ "data_end": data_end,
+ "last_mark_price": last_mark_price,
+ }
+
+
+def count_cluster_buckets(
+ signals: list[dict[str, Any]],
+ *,
+ sim_lookback_days: int,
+ data_end: str,
+) -> dict[str, dict[str, int]]:
+ """sim 기간 내 (side, cluster_size) 빈도."""
+ end_dt = _parse_signal_dt(data_end)
+ start_dt = end_dt - timedelta(days=sim_lookback_days)
+ period = [s for s in signals if _parse_signal_dt(s["datetime"]) >= start_dt]
+ counts: dict[str, dict[str, int]] = {"buy": {}, "sell": {}}
+ for side, cluster in _cluster_signals(period):
+ key = cluster_size_key(len(cluster))
+ bucket = counts.setdefault(side, {})
+ bucket[key] = bucket.get(key, 0) + 1
+ return counts
+
+
+def _rules_for_sim(
+ default_buy: float,
+ default_sell: float,
+ by_cluster: dict[str, dict[str, float]],
+) -> dict[str, Any]:
+ return merge_rules(
+ default_buy=default_buy,
+ default_sell=default_sell,
+ by_cluster=by_cluster,
+ )
+
+
+def _run_sim(
+ signals: list[dict[str, Any]],
+ settings: Settings,
+ data_end: str,
+ last_mark_price: float,
+ *,
+ default_buy: float,
+ default_sell: float,
+ by_cluster: dict[str, dict[str, float]] | None = None,
+) -> dict[str, Any]:
+ rules = _rules_for_sim(default_buy, default_sell, by_cluster or {"buy": {}, "sell": {}})
+ return simulate_gt_signals_pnl(
+ signals=signals,
+ buy_cash_pct=default_buy,
+ sell_coin_pct=default_sell,
+ sizing_rules=rules,
+ **_sim_kwargs(settings, data_end, last_mark_price),
+ )
+
+
+def _best_pct(
+ signals: list[dict[str, Any]],
+ settings: Settings,
+ data_end: str,
+ last_mark_price: float,
+ *,
+ side: str,
+ fixed_buy: float,
+ fixed_sell: float,
+ by_cluster: dict[str, dict[str, float]],
+ bucket_key: str | None = None,
+) -> tuple[float, float]:
+ """한 축(side 또는 bucket)에 대해 최적 pct와 sim 수익률을 반환한다."""
+ best_pct = fixed_buy if side == "buy" else fixed_sell
+ best_return = float("-inf")
+ for pct in PCT_CANDIDATES:
+ buy = fixed_buy
+ sell = fixed_sell
+ cluster = {"buy": dict(by_cluster.get("buy", {})), "sell": dict(by_cluster.get("sell", {}))}
+ if bucket_key is not None:
+ cluster.setdefault(side, {})[bucket_key] = pct
+ else:
+ if side == "buy":
+ buy = pct
+ else:
+ sell = pct
+ sim = _run_sim(
+ signals,
+ settings,
+ data_end,
+ last_mark_price,
+ default_buy=buy,
+ default_sell=sell,
+ by_cluster=cluster,
+ )
+ ret = float(sim.get("total_return_pct") or 0)
+ if ret > best_return:
+ best_return = ret
+ best_pct = pct
+ return best_pct, best_return
+
+
+def tune_sizing_rules(
+ settings: Settings,
+ signals: list[dict[str, Any]],
+ *,
+ data_end: str,
+ last_mark_price: float,
+ technique_id: str,
+ min_bucket_samples: int = 5,
+) -> tuple[dict[str, Any], dict[str, Any]]:
+ """타점 고정 상태에서 전역·클러스터별 매수·매도 비율을 탐색한다."""
+ normalized = normalize_signals_for_sim(signals)
+ base_buy = settings.ops_buy_cash_pct
+ base_sell = settings.ops_sell_coin_pct
+ by_cluster: dict[str, dict[str, float]] = {"buy": {}, "sell": {}}
+ history: list[dict[str, Any]] = []
+
+ buy_pct, buy_ret = _best_pct(
+ normalized,
+ settings,
+ data_end,
+ last_mark_price,
+ side="buy",
+ fixed_buy=base_buy,
+ fixed_sell=base_sell,
+ by_cluster=by_cluster,
+ )
+ history.append({"step": "global_buy", "buy_pct": buy_pct, "return_pct": buy_ret})
+
+ sell_pct, sell_ret = _best_pct(
+ normalized,
+ settings,
+ data_end,
+ last_mark_price,
+ side="sell",
+ fixed_buy=buy_pct,
+ fixed_sell=base_sell,
+ by_cluster=by_cluster,
+ )
+ history.append({"step": "global_sell", "sell_pct": sell_pct, "return_pct": sell_ret})
+
+ buy_pct, buy_ret = _best_pct(
+ normalized,
+ settings,
+ data_end,
+ last_mark_price,
+ side="buy",
+ fixed_buy=buy_pct,
+ fixed_sell=sell_pct,
+ by_cluster=by_cluster,
+ )
+ history.append({"step": "refine_buy", "buy_pct": buy_pct, "return_pct": buy_ret})
+
+ sell_pct, sell_ret = _best_pct(
+ normalized,
+ settings,
+ data_end,
+ last_mark_price,
+ side="sell",
+ fixed_buy=buy_pct,
+ fixed_sell=sell_pct,
+ by_cluster=by_cluster,
+ )
+ history.append({"step": "refine_sell", "sell_pct": sell_pct, "return_pct": sell_ret})
+
+ bucket_counts = count_cluster_buckets(
+ normalized,
+ sim_lookback_days=settings.gt_sim_lookback_days,
+ data_end=data_end,
+ )
+
+ for side in ("buy", "sell"):
+ for key in CLUSTER_SIZE_KEYS:
+ if bucket_counts.get(side, {}).get(key, 0) < min_bucket_samples:
+ continue
+ pct, ret = _best_pct(
+ normalized,
+ settings,
+ data_end,
+ last_mark_price,
+ side=side,
+ fixed_buy=buy_pct,
+ fixed_sell=sell_pct,
+ by_cluster=by_cluster,
+ bucket_key=key,
+ )
+ by_cluster.setdefault(side, {})[key] = pct
+ history.append(
+ {
+ "step": f"bucket_{side}_{key}",
+ "pct": pct,
+ "samples": bucket_counts[side][key],
+ "return_pct": ret,
+ }
+ )
+
+ final_sim = _run_sim(
+ normalized,
+ settings,
+ data_end,
+ last_mark_price,
+ default_buy=buy_pct,
+ default_sell=sell_pct,
+ by_cluster=by_cluster,
+ )
+
+ tuning_meta = {
+ "objective": "total_return_pct",
+ "pct_candidates": list(PCT_CANDIDATES),
+ "min_bucket_samples": min_bucket_samples,
+ "cluster_counts": bucket_counts,
+ "history": history,
+ "baseline_return_pct": _run_sim(
+ normalized,
+ settings,
+ data_end,
+ last_mark_price,
+ default_buy=base_buy,
+ default_sell=base_sell,
+ ).get("total_return_pct"),
+ "final_return_pct": final_sim.get("total_return_pct"),
+ "final_buys_executed": final_sim.get("buys_executed"),
+ "final_sells_executed": final_sim.get("sells_executed"),
+ }
+
+ rules = merge_rules(
+ default_buy=buy_pct,
+ default_sell=sell_pct,
+ by_cluster=by_cluster,
+ technique_id=technique_id,
+ symbol=settings.symbol,
+ tuning=tuning_meta,
+ )
+ return rules, final_sim
diff --git a/src/deepcoin/ground_truth/swing_signals.py b/src/bithumb/ground_truth/swing_signals.py
similarity index 98%
rename from src/deepcoin/ground_truth/swing_signals.py
rename to src/bithumb/ground_truth/swing_signals.py
index ce09a94..f6b47d8 100644
--- a/src/deepcoin/ground_truth/swing_signals.py
+++ b/src/bithumb/ground_truth/swing_signals.py
@@ -6,7 +6,7 @@ from dataclasses import dataclass
import pandas as pd
-from deepcoin.ground_truth.zigzag import Pivot, find_zigzag_pivots
+from bithumb.ground_truth.zigzag import Pivot, find_zigzag_pivots
@dataclass(frozen=True)
diff --git a/src/deepcoin/ground_truth/zigzag.py b/src/bithumb/ground_truth/zigzag.py
similarity index 100%
rename from src/deepcoin/ground_truth/zigzag.py
rename to src/bithumb/ground_truth/zigzag.py
diff --git a/src/bithumb/mtf/__init__.py b/src/bithumb/mtf/__init__.py
new file mode 100644
index 0000000..6eb8ae5
--- /dev/null
+++ b/src/bithumb/mtf/__init__.py
@@ -0,0 +1,23 @@
+"""멀티 타임프레임(MTF) 인과 피처 추출."""
+
+from bithumb.mtf.alignment import as_of_from_signal_bar, last_complete_bar_index
+from bithumb.mtf.extractor import MtfFeatureExtractor, MtfSnapshot
+from bithumb.mtf.filter import MtfSignalFilter, score_mtf_rules
+from bithumb.mtf.rules import MtfRule, MtfRuleSet, derive_rules_from_report, load_mtf_rules, save_mtf_rules
+from bithumb.mtf.store import MTF_INTERVALS, MultiTimeframeStore
+
+__all__ = [
+ "MTF_INTERVALS",
+ "MultiTimeframeStore",
+ "MtfFeatureExtractor",
+ "MtfSnapshot",
+ "MtfSignalFilter",
+ "MtfRule",
+ "MtfRuleSet",
+ "derive_rules_from_report",
+ "load_mtf_rules",
+ "save_mtf_rules",
+ "score_mtf_rules",
+ "as_of_from_signal_bar",
+ "last_complete_bar_index",
+]
diff --git a/src/deepcoin/mtf/alignment.py b/src/bithumb/mtf/alignment.py
similarity index 100%
rename from src/deepcoin/mtf/alignment.py
rename to src/bithumb/mtf/alignment.py
diff --git a/src/deepcoin/mtf/extractor.py b/src/bithumb/mtf/extractor.py
similarity index 94%
rename from src/deepcoin/mtf/extractor.py
rename to src/bithumb/mtf/extractor.py
index 56891dc..ddbcabf 100644
--- a/src/deepcoin/mtf/extractor.py
+++ b/src/bithumb/mtf/extractor.py
@@ -7,9 +7,9 @@ from typing import Any
import pandas as pd
-from deepcoin.mtf.alignment import as_of_from_signal_bar, resolve_bar_index
-from deepcoin.mtf.features import snapshot_at_index
-from deepcoin.mtf.store import MTF_INTERVALS, MultiTimeframeStore
+from bithumb.mtf.alignment import as_of_from_signal_bar, resolve_bar_index
+from bithumb.mtf.features import snapshot_at_index
+from bithumb.mtf.store import MTF_INTERVALS, MultiTimeframeStore
@dataclass
diff --git a/src/deepcoin/mtf/features.py b/src/bithumb/mtf/features.py
similarity index 98%
rename from src/deepcoin/mtf/features.py
rename to src/bithumb/mtf/features.py
index 1fc3cd7..a480ab2 100644
--- a/src/deepcoin/mtf/features.py
+++ b/src/bithumb/mtf/features.py
@@ -6,7 +6,7 @@ from typing import Any
import pandas as pd
-from deepcoin.techniques.indicators import atr, bollinger_bands, ema, macd, rsi
+from bithumb.techniques.indicators import atr, bollinger_bands, ema, macd, rsi
FEATURE_NAMES: tuple[str, ...] = (
"close",
diff --git a/src/deepcoin/mtf/filter.py b/src/bithumb/mtf/filter.py
similarity index 96%
rename from src/deepcoin/mtf/filter.py
rename to src/bithumb/mtf/filter.py
index f5af642..b8fe39a 100644
--- a/src/deepcoin/mtf/filter.py
+++ b/src/bithumb/mtf/filter.py
@@ -4,9 +4,9 @@ from __future__ import annotations
from typing import Any
-from deepcoin.mtf.trend_gate import HtfTrendGate
-from deepcoin.mtf.extractor import MtfFeatureExtractor, MtfSnapshot
-from deepcoin.mtf.rules import MtfRule, MtfRuleSet
+from bithumb.mtf.trend_gate import HtfTrendGate
+from bithumb.mtf.extractor import MtfFeatureExtractor, MtfSnapshot
+from bithumb.mtf.rules import MtfRule, MtfRuleSet
def evaluate_rule(rule: MtfRule, snapshot: MtfSnapshot) -> bool | None:
diff --git a/src/deepcoin/mtf/precompute.py b/src/bithumb/mtf/precompute.py
similarity index 95%
rename from src/deepcoin/mtf/precompute.py
rename to src/bithumb/mtf/precompute.py
index 843483b..1dc0e1e 100644
--- a/src/deepcoin/mtf/precompute.py
+++ b/src/bithumb/mtf/precompute.py
@@ -5,9 +5,9 @@ from __future__ import annotations
import numpy as np
import pandas as pd
-from deepcoin.mtf.alignment import as_of_from_signal_bar
-from deepcoin.mtf.features import snapshot_at_index
-from deepcoin.mtf.store import MultiTimeframeStore
+from bithumb.mtf.alignment import as_of_from_signal_bar
+from bithumb.mtf.features import snapshot_at_index
+from bithumb.mtf.store import MultiTimeframeStore
def _vectorized_tf_indices(
diff --git a/src/deepcoin/mtf/rules.py b/src/bithumb/mtf/rules.py
similarity index 99%
rename from src/deepcoin/mtf/rules.py
rename to src/bithumb/mtf/rules.py
index be45326..30714bd 100644
--- a/src/deepcoin/mtf/rules.py
+++ b/src/bithumb/mtf/rules.py
@@ -7,7 +7,7 @@ from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Literal
-from deepcoin.evaluation.gt_align import GT_SIGNAL_TYPES
+from bithumb.evaluation.gt_align import GT_SIGNAL_TYPES
Operator = Literal["<=", ">="]
diff --git a/src/deepcoin/mtf/store.py b/src/bithumb/mtf/store.py
similarity index 93%
rename from src/deepcoin/mtf/store.py
rename to src/bithumb/mtf/store.py
index 9a5763f..f5834df 100644
--- a/src/deepcoin/mtf/store.py
+++ b/src/bithumb/mtf/store.py
@@ -6,9 +6,9 @@ from pathlib import Path
import pandas as pd
-from deepcoin.data.candle_loader import load_candles
-from deepcoin.data.intervals import DEFAULT_DOWNLOAD_INTERVALS, interval_label
-from deepcoin.mtf.features import compute_feature_frame
+from bithumb.data.candle_loader import load_candles
+from bithumb.data.intervals import DEFAULT_DOWNLOAD_INTERVALS, interval_label
+from bithumb.mtf.features import compute_feature_frame
MTF_INTERVALS: tuple[int, ...] = tuple(DEFAULT_DOWNLOAD_INTERVALS)
diff --git a/src/deepcoin/mtf/trend_gate.py b/src/bithumb/mtf/trend_gate.py
similarity index 100%
rename from src/deepcoin/mtf/trend_gate.py
rename to src/bithumb/mtf/trend_gate.py
diff --git a/src/deepcoin/notifications/__init__.py b/src/bithumb/notifications/__init__.py
similarity index 53%
rename from src/deepcoin/notifications/__init__.py
rename to src/bithumb/notifications/__init__.py
index e52a8ee..41a82ee 100644
--- a/src/deepcoin/notifications/__init__.py
+++ b/src/bithumb/notifications/__init__.py
@@ -1,5 +1,5 @@
"""알림 채널 (텔레그램 등)."""
-from deepcoin.notifications.telegram import TelegramNotifier, create_telegram_notifier
+from bithumb.notifications.telegram import TelegramNotifier, create_telegram_notifier
__all__ = ["TelegramNotifier", "create_telegram_notifier"]
diff --git a/src/deepcoin/notifications/telegram.py b/src/bithumb/notifications/telegram.py
similarity index 86%
rename from src/deepcoin/notifications/telegram.py
rename to src/bithumb/notifications/telegram.py
index 1f1a8ba..a7c6c31 100644
--- a/src/deepcoin/notifications/telegram.py
+++ b/src/bithumb/notifications/telegram.py
@@ -112,7 +112,7 @@ class TelegramNotifier:
equity = cash + coin_qty * price
lines = [
- f"[DeepCoin] {side_label} 체결 ({mode_label})",
+ f"[Bithumb] {side_label} 체결 ({mode_label})",
f"{coin_name} ({symbol}) | {technique_id}",
f"시각: {datetime_str}",
f"신호: {signal_type or side}",
@@ -145,13 +145,36 @@ class TelegramNotifier:
side_label = "매수" if side == "buy" else "매도"
mode_label = "LIVE" if mode == "live" else "PAPER"
text = (
- f"[DeepCoin] {side_label} 실패 ({mode_label})\n"
+ f"[Bithumb] {side_label} 실패 ({mode_label})\n"
f"{symbol} | {technique_id}\n"
f"시각: {datetime_str}\n"
f"사유: {reason}"
)
return self.send_message(text)
+ def notify_ops_error(
+ self,
+ *,
+ mode: str,
+ symbol: str,
+ technique_id: str,
+ stage: str,
+ error: str,
+ detail: str = "",
+ ) -> bool:
+ """운영 tick·체결 등 예외 발생 알림 (프로세스는 계속 실행)."""
+ mode_label = "LIVE" if mode == "live" else "PAPER"
+ lines = [
+ f"[Bithumb] 운영 오류 ({mode_label})",
+ f"{symbol} | {technique_id}",
+ f"단계: {stage}",
+ f"원인: {error}",
+ ]
+ if detail:
+ lines.append(detail)
+ lines.append("프로세스는 계속 실행됩니다.")
+ return self.send_message("\n".join(lines))
+
def _fmt_krw(value: float) -> str:
"""원화 금액 포맷."""
diff --git a/src/bithumb/operations/__init__.py b/src/bithumb/operations/__init__.py
new file mode 100644
index 0000000..4659dd1
--- /dev/null
+++ b/src/bithumb/operations/__init__.py
@@ -0,0 +1,12 @@
+"""현물 3단계 운영 — composite_v3 + MTF."""
+
+from bithumb.operations.backtest import run_filtered_backtest, save_backtest_report
+from bithumb.operations.runner import OperationsRunner
+from bithumb.operations.signal_pipeline import run_signal_pipeline
+
+__all__ = [
+ "OperationsRunner",
+ "run_filtered_backtest",
+ "run_signal_pipeline",
+ "save_backtest_report",
+]
diff --git a/src/deepcoin/operations/backtest.py b/src/bithumb/operations/backtest.py
similarity index 63%
rename from src/deepcoin/operations/backtest.py
rename to src/bithumb/operations/backtest.py
index 6bd4151..f5bd563 100644
--- a/src/deepcoin/operations/backtest.py
+++ b/src/bithumb/operations/backtest.py
@@ -7,10 +7,11 @@ from datetime import datetime
from pathlib import Path
from typing import Any
-from deepcoin.config import Settings
-from deepcoin.evaluation.causal_sim import normalize_signals_for_sim
-from deepcoin.ground_truth.pnl import simulate_gt_signals_pnl
-from deepcoin.operations.signal_pipeline import run_signal_pipeline
+from bithumb.config import Settings
+from bithumb.evaluation.causal_sim import normalize_signals_for_sim
+from bithumb.ground_truth.pnl import simulate_gt_signals_pnl
+from bithumb.ground_truth.sizing_rules import load_sizing_rules
+from bithumb.operations.signal_pipeline import run_signal_pipeline
def run_filtered_backtest(settings: Settings) -> dict[str, Any]:
@@ -22,28 +23,25 @@ def run_filtered_backtest(settings: Settings) -> dict[str, Any]:
)
kept = pipeline["kept"]
normalized = normalize_signals_for_sim(kept)
- sim = simulate_gt_signals_pnl(
- signals=normalized,
- initial_cash_krw=settings.gt_initial_cash_krw,
- fee_rate=settings.gt_trading_fee_rate,
- min_order_krw=settings.ops_min_order_krw,
- slippage_rate=settings.ops_slippage_rate,
- daily_max_trades=settings.ops_daily_max_trades,
- sim_lookback_days=settings.gt_sim_lookback_days,
- data_end=pipeline["data_end"],
- last_mark_price=pipeline["last_price"],
- )
+ sizing_rules = load_sizing_rules(settings.ops_sizing_rules_json)
+ sim_kwargs = {
+ "initial_cash_krw": settings.gt_initial_cash_krw,
+ "fee_rate": settings.gt_trading_fee_rate,
+ "min_order_krw": settings.ops_min_order_krw,
+ "slippage_rate": settings.ops_slippage_rate,
+ "daily_max_trades": settings.ops_daily_max_trades,
+ "buy_cash_pct": settings.ops_buy_cash_pct,
+ "sell_coin_pct": settings.ops_sell_coin_pct,
+ "sizing_rules": sizing_rules,
+ "sim_lookback_days": settings.gt_sim_lookback_days,
+ "data_end": pipeline["data_end"],
+ "last_mark_price": pipeline["last_price"],
+ }
+ sim = simulate_gt_signals_pnl(signals=normalized, **sim_kwargs)
raw_sim = simulate_gt_signals_pnl(
signals=normalize_signals_for_sim(pipeline["scoped_raw_signals"]),
- initial_cash_krw=settings.gt_initial_cash_krw,
- fee_rate=settings.gt_trading_fee_rate,
- min_order_krw=settings.ops_min_order_krw,
- slippage_rate=settings.ops_slippage_rate,
- daily_max_trades=settings.ops_daily_max_trades,
- sim_lookback_days=settings.gt_sim_lookback_days,
- data_end=pipeline["data_end"],
- last_mark_price=pipeline["last_price"],
+ **sim_kwargs,
)
return {
diff --git a/src/deepcoin/operations/candle_sync.py b/src/bithumb/operations/candle_sync.py
similarity index 90%
rename from src/deepcoin/operations/candle_sync.py
rename to src/bithumb/operations/candle_sync.py
index b936422..c4a02b4 100644
--- a/src/deepcoin/operations/candle_sync.py
+++ b/src/bithumb/operations/candle_sync.py
@@ -5,9 +5,9 @@ from __future__ import annotations
import logging
from dataclasses import replace
-from deepcoin.config import Settings
-from deepcoin.data.candle_store import CandleStore
-from deepcoin.data.downloader import CandleDownloader, DownloadResult
+from bithumb.config import Settings
+from bithumb.data.candle_store import CandleStore
+from bithumb.data.downloader import CandleDownloader, DownloadResult
logger = logging.getLogger(__name__)
diff --git a/src/deepcoin/operations/chart.py b/src/bithumb/operations/chart.py
similarity index 81%
rename from src/deepcoin/operations/chart.py
rename to src/bithumb/operations/chart.py
index def6ee5..4fb6cce 100644
--- a/src/deepcoin/operations/chart.py
+++ b/src/bithumb/operations/chart.py
@@ -5,10 +5,10 @@ from __future__ import annotations
from pathlib import Path
from typing import Any
-from deepcoin.config import Settings
-from deepcoin.data.intervals import interval_label
-from deepcoin.ground_truth.chart import render_ground_truth_sim_chart
-from deepcoin.operations.backtest import run_filtered_backtest
+from bithumb.config import Settings
+from bithumb.data.intervals import interval_label
+from bithumb.ground_truth.chart import render_ground_truth_sim_chart
+from bithumb.operations.backtest import run_filtered_backtest
def _ops_chart_shell(
@@ -19,6 +19,12 @@ def _ops_chart_shell(
"""운영 백테스트 차트용 GT 메타 셸을 구성한다."""
slip_pct = settings.ops_slippage_rate * 100
daily_cap = settings.ops_daily_max_trades
+ sizing_note = ""
+ if sim_pnl.get("sizing_rules_applied"):
+ lb = sim_pnl.get("learned_default_buy_cash_pct")
+ ls = sim_pnl.get("learned_default_sell_coin_pct")
+ if lb is not None and ls is not None:
+ sizing_note = f" · 학습 비율 {float(lb) * 100:.0f}%/{float(ls) * 100:.0f}%"
return {
"meta": {
"symbol": settings.symbol,
@@ -34,6 +40,7 @@ def _ops_chart_shell(
"ops_note": (
f"슬리피지 {slip_pct:.2f}% · 일 체결 상한 {daily_cap} · "
f"MTF {'on' if pipeline.get('mtf_enabled') else 'off'}"
+ f"{sizing_note}"
),
"data_from": sim_pnl.get("period_from"),
"data_to": sim_pnl.get("period_to"),
diff --git a/src/deepcoin/operations/execution.py b/src/bithumb/operations/execution.py
similarity index 100%
rename from src/deepcoin/operations/execution.py
rename to src/bithumb/operations/execution.py
diff --git a/src/deepcoin/operations/executor.py b/src/bithumb/operations/executor.py
similarity index 70%
rename from src/deepcoin/operations/executor.py
rename to src/bithumb/operations/executor.py
index 4a1865d..752c842 100644
--- a/src/deepcoin/operations/executor.py
+++ b/src/bithumb/operations/executor.py
@@ -6,14 +6,16 @@ import logging
from abc import ABC, abstractmethod
from typing import Any
-from deepcoin.api.bithumb_private import BithumbPrivateClient
-from deepcoin.config import Settings
-from deepcoin.operations.execution import fill_price
-from deepcoin.operations.trade_engine import (
+from bithumb.api.bithumb_private import BithumbPrivateClient
+from bithumb.config import Settings
+from bithumb.ground_truth.sizing_rules import load_sizing_rules
+from bithumb.operations.execution import fill_price
+from bithumb.operations.trade_engine import (
TradeResult,
apply_trade_to_portfolio,
compute_buy_order,
compute_sell_order,
+ spendable_cash_for_exchange_buy,
)
logger = logging.getLogger(__name__)
@@ -38,6 +40,7 @@ class PaperExecutor(OrderExecutor):
def __init__(self, settings: Settings) -> None:
self.settings = settings
+ self.sizing_rules = load_sizing_rules(settings.ops_sizing_rules_json)
def execute_signal(
self,
@@ -66,6 +69,8 @@ class PaperExecutor(OrderExecutor):
fee_rate=fee_rate,
min_order_krw=min_order,
cluster_size=cluster_size,
+ buy_cash_pct=self.settings.ops_buy_cash_pct,
+ sizing_rules=self.sizing_rules,
)
else:
trade = compute_sell_order(
@@ -74,6 +79,8 @@ class PaperExecutor(OrderExecutor):
fee_rate=fee_rate,
min_order_krw=min_order,
cluster_size=cluster_size,
+ sell_coin_pct=self.settings.ops_sell_coin_pct,
+ sizing_rules=self.sizing_rules,
)
if trade.executed:
@@ -87,6 +94,7 @@ class LiveExecutor(OrderExecutor):
def __init__(self, settings: Settings, client: BithumbPrivateClient) -> None:
self.settings = settings
self.client = client
+ self.sizing_rules = load_sizing_rules(settings.ops_sizing_rules_json)
def _sync_portfolio(self, portfolio: dict[str, Any]) -> None:
"""거래소 잔고로 포트폴리오를 동기화한다."""
@@ -116,27 +124,46 @@ class LiveExecutor(OrderExecutor):
coin = float(portfolio.get("coin_qty", 0))
if side == "buy":
+ spendable = spendable_cash_for_exchange_buy(
+ cash,
+ self.settings.ops_exchange_fee_lock_rate,
+ self.settings.ops_buy_safety_buffer_krw,
+ )
trade = compute_buy_order(
- cash_krw=cash,
+ cash_krw=spendable,
coin_qty=coin,
price=price,
fee_rate=fee_rate,
min_order_krw=min_order,
cluster_size=cluster_size,
+ buy_cash_pct=self.settings.ops_buy_cash_pct,
+ sizing_rules=self.sizing_rules,
)
if not trade.executed:
+ if trade.expected_skip:
+ logger.info(
+ "live buy skip (%s): cash=%.0f spendable=%.0f coin=%.8f",
+ trade.skip_reason,
+ cash,
+ spendable,
+ coin,
+ )
return trade
try:
resp = self.client.market_buy_krw(self.settings.market, trade.order_krw)
trade.api_response = resp
self._sync_portfolio(portfolio)
except Exception as exc:
- logger.exception("live buy failed")
+ logger.exception(
+ "live buy failed order_krw=%.0f cash=%.0f",
+ trade.order_krw,
+ cash,
+ )
return TradeResult(
executed=False,
side="buy",
- order_krw=0.0,
- order_coin=0.0,
+ order_krw=trade.order_krw,
+ order_coin=trade.order_coin,
fee_krw=0.0,
price=price,
skip_reason=str(exc),
@@ -149,20 +176,33 @@ class LiveExecutor(OrderExecutor):
fee_rate=fee_rate,
min_order_krw=min_order,
cluster_size=cluster_size,
+ sell_coin_pct=self.settings.ops_sell_coin_pct,
+ sizing_rules=self.sizing_rules,
)
if not trade.executed:
+ if trade.expected_skip:
+ logger.info(
+ "live sell skip (%s): %s=%.8f cash=%.0f",
+ trade.skip_reason,
+ self.settings.symbol,
+ coin,
+ cash,
+ )
return trade
try:
resp = self.client.market_sell_volume(self.settings.market, trade.order_coin)
trade.api_response = resp
self._sync_portfolio(portfolio)
except Exception as exc:
- logger.exception("live sell failed")
+ logger.exception(
+ "live sell failed order_coin=%.8f",
+ trade.order_coin,
+ )
return TradeResult(
executed=False,
side="sell",
- order_krw=0.0,
- order_coin=0.0,
+ order_krw=trade.order_krw,
+ order_coin=trade.order_coin,
fee_krw=0.0,
price=price,
skip_reason=str(exc),
diff --git a/src/bithumb/operations/live_bootstrap.py b/src/bithumb/operations/live_bootstrap.py
new file mode 100644
index 0000000..f41e1c1
--- /dev/null
+++ b/src/bithumb/operations/live_bootstrap.py
@@ -0,0 +1,253 @@
+"""live 운영 전 사전 점검 및 상태 초기화."""
+
+from __future__ import annotations
+
+import json
+import shutil
+from datetime import datetime
+from pathlib import Path
+from typing import Any
+
+from bithumb.api.bithumb_private import BithumbPrivateClient
+from bithumb.config import Settings
+from bithumb.notifications.telegram import create_telegram_notifier
+from bithumb.operations.state_store import load_state, save_state
+
+
+def create_bithumb_client(settings: Settings) -> BithumbPrivateClient:
+ """설정 기반 빗썸 Private 클라이언트를 생성한다."""
+ return BithumbPrivateClient(
+ access_key=settings.bithumb_access_key,
+ secret_key=settings.bithumb_secret_key,
+ base_url=settings.api_url,
+ sleep_sec=settings.request_sleep_sec,
+ retries=settings.request_retries,
+ )
+
+
+def sync_portfolio_from_exchange(
+ portfolio: dict[str, Any],
+ client: BithumbPrivateClient,
+ symbol: str,
+ *,
+ mode: str = "live",
+) -> dict[str, float]:
+ """거래소 잔고로 포트폴리오를 갱신한다.
+
+ Returns:
+ {"cash_krw", "coin_qty"} 스냅샷.
+ """
+ krw_avail, krw_locked = client.get_balance("KRW")
+ coin_avail, coin_locked = client.get_balance(symbol)
+ portfolio["cash_krw"] = krw_avail
+ portfolio["coin_qty"] = coin_avail
+ portfolio["mode"] = mode
+ return {
+ "cash_krw": krw_avail,
+ "cash_locked_krw": krw_locked,
+ "coin_qty": coin_avail,
+ "coin_locked_qty": coin_locked,
+ }
+
+
+def backup_state_file(state_path: Path) -> Path | None:
+ """기존 운영 상태 JSON을 백업한다."""
+ if not state_path.exists():
+ return None
+ stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
+ backup = state_path.with_name(f"{state_path.stem}.backup_{stamp}{state_path.suffix}")
+ shutil.copy2(state_path, backup)
+ return backup
+
+
+def init_live_state(
+ settings: Settings,
+ *,
+ backup: bool = True,
+ preserve_bar_cursor: bool = True,
+) -> dict[str, Any]:
+ """paper 상태를 live로 전환하고 거래소 잔고를 반영한다."""
+ state_path = settings.ops_state_json
+ backup_path: Path | None = None
+ if backup and state_path.exists():
+ backup_path = backup_state_file(state_path)
+
+ state = load_state(state_path, initial_cash_krw=settings.gt_initial_cash_krw)
+ if not preserve_bar_cursor:
+ state["last_processed_bar_index"] = -1
+ state["last_processed_datetime"] = None
+
+ client = create_bithumb_client(settings)
+ balances = sync_portfolio_from_exchange(
+ state["portfolio"],
+ client,
+ settings.symbol,
+ mode="live",
+ )
+
+ state["live_initialized_at"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ state["live_init_backup"] = str(backup_path) if backup_path else None
+ save_state(state_path, state)
+
+ return {
+ "state_path": str(state_path),
+ "backup_path": str(backup_path) if backup_path else None,
+ "balances": balances,
+ "last_processed_bar_index": state.get("last_processed_bar_index", -1),
+ }
+
+
+def run_preflight(settings: Settings) -> dict[str, Any]:
+ """live 운영 전 점검 항목을 실행한다."""
+ checks: list[dict[str, Any]] = []
+ ok = True
+
+ def add(name: str, passed: bool, detail: str, *, required: bool = True) -> None:
+ nonlocal ok
+ if required and not passed:
+ ok = False
+ checks.append(
+ {
+ "name": name,
+ "passed": passed,
+ "required": required,
+ "detail": detail,
+ }
+ )
+
+ add(
+ "ops_mode_live",
+ settings.ops_mode == "live",
+ f"OPS_MODE={settings.ops_mode}",
+ )
+ add(
+ "api_keys",
+ bool(settings.bithumb_access_key and settings.bithumb_secret_key),
+ "BITHUMB_ACCESS_KEY / BITHUMB_SECRET_KEY 설정",
+ )
+ add(
+ "technique",
+ bool(settings.ops_technique_id),
+ f"OPS_TECHNIQUE_ID={settings.ops_technique_id}",
+ )
+
+ technique_path = settings.techniques_dir / f"{settings.ops_technique_id}.json"
+ add(
+ "technique_cache",
+ technique_path.exists(),
+ str(technique_path),
+ )
+
+ add(
+ "db_exists",
+ settings.db_path.exists(),
+ str(settings.db_path),
+ )
+
+ add(
+ "state_path_writable",
+ True,
+ str(settings.ops_state_json),
+ )
+ settings.ops_state_json.parent.mkdir(parents=True, exist_ok=True)
+ settings.ops_report_json.parent.mkdir(parents=True, exist_ok=True)
+
+ balances: dict[str, float] | None = None
+ if settings.bithumb_access_key and settings.bithumb_secret_key:
+ try:
+ client = create_bithumb_client(settings)
+ accounts = client.get_accounts()
+ add(
+ "api_connectivity",
+ isinstance(accounts, list),
+ f"accounts={len(accounts)}",
+ )
+ balances = sync_portfolio_from_exchange(
+ {"cash_krw": 0.0, "coin_qty": 0.0},
+ client,
+ settings.symbol,
+ )
+ add(
+ "krw_balance",
+ balances["cash_krw"] >= settings.ops_min_order_krw,
+ (
+ f"가용 {_fmt_krw(balances['cash_krw'])} "
+ f"(최소 주문 {_fmt_krw(settings.ops_min_order_krw)})"
+ ),
+ )
+ add(
+ "coin_balance",
+ True,
+ (
+ f"{settings.symbol} {balances['coin_qty']:.8f} "
+ f"(locked {balances['coin_locked_qty']:.8f})"
+ ),
+ required=False,
+ )
+ except Exception as exc:
+ add("api_connectivity", False, str(exc))
+ else:
+ add("api_connectivity", False, "API 키 없음")
+
+ telegram = create_telegram_notifier(
+ settings.telegram_bot_token,
+ settings.telegram_chat_id,
+ enabled=settings.ops_telegram_enabled,
+ )
+ telegram_ok = False
+ if telegram.is_active:
+ telegram_ok = telegram.send_message(
+ "[Bithumb] live 사전 점검 — 텔레그램 연결 OK"
+ )
+ add(
+ "telegram",
+ telegram_ok if telegram.is_active else True,
+ "활성" if telegram.is_active else "비활성(선택)",
+ required=telegram.is_active,
+ )
+
+ backtest_path = settings.ops_filtered_backtest_json
+ add(
+ "backtest_report",
+ backtest_path.exists(),
+ str(backtest_path),
+ required=False,
+ )
+
+ add(
+ "daily_max_trades",
+ settings.ops_daily_max_trades > 0,
+ str(settings.ops_daily_max_trades),
+ )
+ add(
+ "slippage_rate",
+ settings.ops_slippage_rate >= 0,
+ f"{settings.ops_slippage_rate * 100:.3f}%",
+ )
+
+ return {
+ "ok": ok,
+ "symbol": settings.symbol,
+ "market": settings.market,
+ "technique_id": settings.ops_technique_id,
+ "mode": settings.ops_mode,
+ "balances": balances,
+ "checks": checks,
+ }
+
+
+def save_preflight_report(path: Path, report: dict[str, Any]) -> Path:
+ """사전 점검 결과를 JSON으로 저장한다."""
+ path.parent.mkdir(parents=True, exist_ok=True)
+ payload = {
+ **report,
+ "generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+ }
+ with path.open("w", encoding="utf-8") as fp:
+ json.dump(payload, fp, ensure_ascii=False, indent=2)
+ return path
+
+
+def _fmt_krw(value: float) -> str:
+ """원화 금액 포맷."""
+ return f"{round(value):,}원"
diff --git a/src/deepcoin/operations/runner.py b/src/bithumb/operations/runner.py
similarity index 62%
rename from src/deepcoin/operations/runner.py
rename to src/bithumb/operations/runner.py
index 5e57344..8861919 100644
--- a/src/deepcoin/operations/runner.py
+++ b/src/bithumb/operations/runner.py
@@ -5,21 +5,24 @@ from __future__ import annotations
import json
import logging
import time
+import traceback
from datetime import datetime
from pathlib import Path
from typing import Any
-from deepcoin.config import Settings
-from deepcoin.ground_truth.pnl import _cluster_signals
-from deepcoin.notifications.telegram import create_telegram_notifier
-from deepcoin.operations.candle_sync import sync_ops_candles
-from deepcoin.operations.executor import create_executor
-from deepcoin.operations.signal_pipeline import (
+from bithumb.config import Settings
+from bithumb.ground_truth.pnl import _cluster_signals
+from bithumb.notifications.telegram import create_telegram_notifier
+from bithumb.operations.candle_sync import sync_ops_candles
+from bithumb.operations.executor import LiveExecutor, create_executor
+from bithumb.operations.live_bootstrap import sync_portfolio_from_exchange
+from bithumb.operations.signal_pipeline import (
filter_signals_for_ops,
generate_raw_signals,
load_ops_candles,
)
-from deepcoin.operations.state_store import load_state, reset_daily_trade_count, save_state
+from bithumb.operations.state_store import load_state, reset_daily_trade_count, save_state
+from bithumb.operations.trade_engine import TradeResult
logger = logging.getLogger(__name__)
@@ -91,9 +94,30 @@ class OperationsRunner:
initial_cash_krw=settings.gt_initial_cash_krw,
)
self.state["portfolio"]["mode"] = settings.ops_mode
+ if settings.ops_mode == "live" and isinstance(self.executor, LiveExecutor):
+ try:
+ sync_portfolio_from_exchange(
+ self.state["portfolio"],
+ self.executor.client,
+ settings.symbol,
+ mode="live",
+ )
+ save_state(settings.ops_state_json, self.state)
+ except Exception as exc:
+ logger.exception("live 초기 잔고 동기화 실패")
+ self._notify_ops_error("init_sync", exc)
def tick(self, *, sync_candles: bool | None = None) -> dict[str, Any]:
- """신호 확인 및 체결 1회."""
+ """신호 확인 및 체결 1회. 예외 발생 시 텔레그램 알림 후 error 리포트 반환."""
+ try:
+ return self._tick_impl(sync_candles=sync_candles)
+ except Exception as exc:
+ logger.exception("운영 tick 실패")
+ self._notify_ops_error("tick", exc)
+ return self._build_error_report(exc, stage="tick")
+
+ def _tick_impl(self, *, sync_candles: bool | None = None) -> dict[str, Any]:
+ """tick 본체."""
do_sync = sync_candles if sync_candles is not None else self.settings.ops_sync_candles
candle_sync_results: list[Any] = []
if do_sync:
@@ -109,6 +133,18 @@ class OperationsRunner:
kept = filtered["kept"]
reset_daily_trade_count(self.state)
+ if self.settings.ops_mode == "live" and isinstance(self.executor, LiveExecutor):
+ try:
+ sync_portfolio_from_exchange(
+ self.state["portfolio"],
+ self.executor.client,
+ self.settings.symbol,
+ mode="live",
+ )
+ except Exception as exc:
+ logger.exception("tick 잔고 동기화 실패")
+ self._notify_ops_error("portfolio_sync", exc)
+
last_bar = int(self.state.get("last_processed_bar_index", -1))
target_bars = _pending_bar_indices(kept, last_bar, latest_bar)
@@ -128,11 +164,32 @@ class OperationsRunner:
(k for k in kept if k["datetime"] == sig["datetime"]),
sig,
)
- trade = self.executor.execute_signal(
- full_sig,
- self.state["portfolio"],
- cluster_size=cluster_size,
- )
+ try:
+ trade = self.executor.execute_signal(
+ full_sig,
+ self.state["portfolio"],
+ cluster_size=cluster_size,
+ )
+ except Exception as exc:
+ logger.exception(
+ "체결 실행 예외 side=%s datetime=%s",
+ full_sig.get("side"),
+ full_sig.get("datetime"),
+ )
+ self._notify_ops_error(
+ "execute",
+ exc,
+ context=f"side={full_sig.get('side')} dt={full_sig.get('datetime')}",
+ )
+ trade = TradeResult(
+ executed=False,
+ side=str(full_sig.get("side", "")),
+ order_krw=0.0,
+ order_coin=0.0,
+ fee_krw=0.0,
+ price=float(full_sig.get("price", 0)),
+ skip_reason=str(exc),
+ )
record = {
"datetime": full_sig["datetime"],
"side": full_sig["side"],
@@ -154,6 +211,7 @@ class OperationsRunner:
elif (
self.settings.ops_mode == "live"
and trade.skip_reason
+ and not trade.expected_skip
and self.telegram.is_active
):
self.telegram.notify_trade_failure(
@@ -213,6 +271,50 @@ class OperationsRunner:
self._save_report(report)
return report
+ def _notify_ops_error(
+ self,
+ stage: str,
+ exc: Exception,
+ *,
+ context: str = "",
+ ) -> None:
+ """운영 예외를 텔레그램으로 알린다."""
+ if not self.telegram.is_active:
+ return
+ tb_tail = traceback.format_exc(limit=4).strip()
+ detail_parts = [p for p in (context, tb_tail) if p]
+ self.telegram.notify_ops_error(
+ mode=self.settings.ops_mode,
+ symbol=self.settings.symbol,
+ technique_id=self.settings.ops_technique_id,
+ stage=stage,
+ error=str(exc),
+ detail="\n".join(detail_parts)[:800],
+ )
+
+ def _build_error_report(self, exc: Exception, *, stage: str) -> dict[str, Any]:
+ """tick 실패 시 저장·표시용 리포트."""
+ now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ self.state["last_run_at"] = now
+ report = {
+ "generated_at": now,
+ "mode": self.settings.ops_mode,
+ "technique_id": self.settings.ops_technique_id,
+ "error": True,
+ "error_stage": stage,
+ "error_message": str(exc),
+ "executions": [],
+ "portfolio": self.state.get("portfolio", {}),
+ "trades_today_count": self.state.get("trades_today_count", 0),
+ "last_processed_bar_index": self.state.get("last_processed_bar_index", -1),
+ }
+ try:
+ save_state(self.settings.ops_state_json, self.state)
+ self._save_report(report)
+ except Exception:
+ logger.exception("error 리포트 저장 실패")
+ return report
+
def _notify_trade(
self,
signal: dict[str, Any],
@@ -243,5 +345,9 @@ class OperationsRunner:
"""최신 운영 리포트 저장."""
path = self.settings.ops_report_json
path.parent.mkdir(parents=True, exist_ok=True)
- with path.open("w", encoding="utf-8") as fp:
- json.dump(report, fp, ensure_ascii=False, indent=2)
+ try:
+ with path.open("w", encoding="utf-8") as fp:
+ json.dump(report, fp, ensure_ascii=False, indent=2)
+ except OSError as exc:
+ logger.exception("ops_report 저장 실패: %s", path)
+ self._notify_ops_error("save_report", exc, context=str(path))
diff --git a/src/deepcoin/operations/signal_pipeline.py b/src/bithumb/operations/signal_pipeline.py
similarity index 94%
rename from src/deepcoin/operations/signal_pipeline.py
rename to src/bithumb/operations/signal_pipeline.py
index feecce3..607813a 100644
--- a/src/deepcoin/operations/signal_pipeline.py
+++ b/src/bithumb/operations/signal_pipeline.py
@@ -10,17 +10,17 @@ from typing import Any
import pandas as pd
-from deepcoin.config import Settings
-from deepcoin.data.candle_loader import load_candles
-from deepcoin.mtf.extractor import MtfFeatureExtractor
-from deepcoin.mtf.filter import MtfSignalFilter
-from deepcoin.mtf.rules import load_or_derive_mtf_rules
-from deepcoin.mtf.store import MultiTimeframeStore
-from deepcoin.mtf.trend_gate import HtfTrendGate
-from deepcoin.operations.signal_type import enrich_signal_types
-from deepcoin.techniques.base import TechniqueParams, TechniqueResult
-from deepcoin.techniques.registry import get_technique
-from deepcoin.techniques.runner import load_technique_result, run_technique, save_technique_result
+from bithumb.config import Settings
+from bithumb.data.candle_loader import load_candles
+from bithumb.mtf.extractor import MtfFeatureExtractor
+from bithumb.mtf.filter import MtfSignalFilter
+from bithumb.mtf.rules import load_or_derive_mtf_rules
+from bithumb.mtf.store import MultiTimeframeStore
+from bithumb.mtf.trend_gate import HtfTrendGate
+from bithumb.operations.signal_type import enrich_signal_types
+from bithumb.techniques.base import TechniqueParams, TechniqueResult
+from bithumb.techniques.registry import get_technique
+from bithumb.techniques.runner import load_technique_result, run_technique, save_technique_result
logger = logging.getLogger(__name__)
diff --git a/src/deepcoin/operations/signal_type.py b/src/bithumb/operations/signal_type.py
similarity index 100%
rename from src/deepcoin/operations/signal_type.py
rename to src/bithumb/operations/signal_type.py
diff --git a/src/deepcoin/operations/state_store.py b/src/bithumb/operations/state_store.py
similarity index 100%
rename from src/deepcoin/operations/state_store.py
rename to src/bithumb/operations/state_store.py
diff --git a/src/deepcoin/operations/trade_engine.py b/src/bithumb/operations/trade_engine.py
similarity index 58%
rename from src/deepcoin/operations/trade_engine.py
rename to src/bithumb/operations/trade_engine.py
index 1937252..f536c1e 100644
--- a/src/deepcoin/operations/trade_engine.py
+++ b/src/bithumb/operations/trade_engine.py
@@ -2,10 +2,12 @@
from __future__ import annotations
+import math
from dataclasses import dataclass
from typing import Any
-from deepcoin.ground_truth.order_sizing import max_buy_from_cash
+from bithumb.ground_truth.order_sizing import max_buy_from_cash, max_sell_coin_qty
+from bithumb.ground_truth.sizing_rules import resolve_buy_cash_pct, resolve_sell_coin_pct
@dataclass
@@ -19,6 +21,7 @@ class TradeResult:
fee_krw: float
price: float
skip_reason: str = ""
+ expected_skip: bool = False
api_response: dict[str, Any] | None = None
def to_dict(self) -> dict[str, Any]:
@@ -31,10 +34,32 @@ class TradeResult:
"fee_krw": round(self.fee_krw, 2),
"price": self.price,
"skip_reason": self.skip_reason,
+ "expected_skip": self.expected_skip,
"api_response": self.api_response,
}
+def spendable_cash_for_exchange_buy(
+ cash_krw: float,
+ fee_lock_rate: float = 0.0025,
+ safety_buffer_krw: float = 1000.0,
+) -> float:
+ """거래소 시장가 매수 시 주문 가능 원화 (수수료 lock + floor + 안전 여유).
+
+ 빗썸은 주문금액 + 예약수수료를 KRW에서 동시에 lock하므로
+ 가용 원화 전액을 price로 넣으면 insufficient_funds(400)가 발생한다.
+ ``floor(가용/(1+lock)) - safety_buffer_krw`` 로 주문 상한을 계산한다.
+ """
+ cash = max(float(cash_krw), 0.0)
+ lock = max(float(fee_lock_rate), 0.0)
+ buffer = max(float(safety_buffer_krw), 0.0)
+ if lock <= 0:
+ spendable = cash
+ else:
+ spendable = cash / (1.0 + lock)
+ return max(0.0, math.floor(spendable) - buffer)
+
+
def compute_buy_order(
*,
cash_krw: float,
@@ -43,13 +68,16 @@ def compute_buy_order(
fee_rate: float,
min_order_krw: float,
cluster_size: int = 1,
+ buy_cash_pct: float | None = None,
+ sizing_rules: dict | None = None,
) -> TradeResult:
"""매수 주문 금액·수량을 계산한다."""
cash = max(float(cash_krw), 0.0)
equity = cash + float(coin_qty) * price
- cash_cap = max_buy_from_cash(equity, cash)
- per_buy = cash / cluster_size if cluster_size > 0 else cash
- order_krw = min(per_buy, cash, cash_cap)
+ effective_pct = resolve_buy_cash_pct(sizing_rules, cluster_size, buy_cash_pct)
+ cash_cap = max_buy_from_cash(equity, cash, base_pct=effective_pct)
+ per_buy = cash_cap / cluster_size if cluster_size > 0 else cash_cap
+ order_krw = math.floor(min(per_buy, cash, cash_cap))
if order_krw < min_order_krw:
return TradeResult(
@@ -60,6 +88,7 @@ def compute_buy_order(
fee_krw=0.0,
price=price,
skip_reason="원화 부족 또는 최소 주문 미만",
+ expected_skip=True,
)
fee = order_krw * fee_rate
@@ -81,13 +110,29 @@ def compute_sell_order(
fee_rate: float,
min_order_krw: float,
cluster_size: int = 1,
+ sell_coin_pct: float | None = None,
+ sizing_rules: dict | None = None,
) -> TradeResult:
"""매도 주문 수량을 계산한다."""
qty = max(float(coin_qty), 0.0)
- per_sell = qty / cluster_size if cluster_size > 0 else qty
- order_coin = per_sell
+ effective_pct = resolve_sell_coin_pct(sizing_rules, cluster_size, sell_coin_pct)
+ sellable = max_sell_coin_qty(qty, sell_pct=effective_pct)
+ per_sell = sellable / cluster_size if cluster_size > 0 else sellable
+ order_coin = min(per_sell, qty)
order_krw = order_coin * price
+ if qty <= 0:
+ return TradeResult(
+ executed=False,
+ side="sell",
+ order_krw=0.0,
+ order_coin=0.0,
+ fee_krw=0.0,
+ price=price,
+ skip_reason="보유 코인 없음",
+ expected_skip=True,
+ )
+
if order_coin <= 0 or order_krw < min_order_krw:
return TradeResult(
executed=False,
@@ -96,7 +141,8 @@ def compute_sell_order(
order_coin=0.0,
fee_krw=0.0,
price=price,
- skip_reason="코인 부족 또는 최소 주문 미만",
+ skip_reason="매도 가능 수량 부족(최소 주문 미만)",
+ expected_skip=True,
)
fee = order_krw * fee_rate
diff --git a/src/deepcoin/techniques/__init__.py b/src/bithumb/techniques/__init__.py
similarity index 77%
rename from src/deepcoin/techniques/__init__.py
rename to src/bithumb/techniques/__init__.py
index 52d9655..979c655 100644
--- a/src/deepcoin/techniques/__init__.py
+++ b/src/bithumb/techniques/__init__.py
@@ -1,13 +1,13 @@
"""2단계: Ground Truth 정합 매매 기법."""
-from deepcoin.techniques.registry import (
+from bithumb.techniques.registry import (
get_all_techniques,
get_composite_techniques,
get_single_techniques,
list_technique_ids,
techniques_by_category,
)
-from deepcoin.techniques.runner import run_all_techniques, run_technique
+from bithumb.techniques.runner import run_all_techniques, run_technique
__all__ = [
"get_all_techniques",
diff --git a/src/deepcoin/techniques/adx_trend.py b/src/bithumb/techniques/adx_trend.py
similarity index 90%
rename from src/deepcoin/techniques/adx_trend.py
rename to src/bithumb/techniques/adx_trend.py
index 0c68df6..b59ecf7 100644
--- a/src/deepcoin/techniques/adx_trend.py
+++ b/src/bithumb/techniques/adx_trend.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
-from deepcoin.techniques.indicators import adx
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
+from bithumb.techniques.indicators import adx
class AdxTrendTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/atr_channel.py b/src/bithumb/techniques/atr_channel.py
similarity index 91%
rename from src/deepcoin/techniques/atr_channel.py
rename to src/bithumb/techniques/atr_channel.py
index 648329c..bce02b2 100644
--- a/src/deepcoin/techniques/atr_channel.py
+++ b/src/bithumb/techniques/atr_channel.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
-from deepcoin.techniques.indicators import atr, ema
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
+from bithumb.techniques.indicators import atr, ema
class AtrChannelTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/base.py b/src/bithumb/techniques/base.py
similarity index 100%
rename from src/deepcoin/techniques/base.py
rename to src/bithumb/techniques/base.py
diff --git a/src/deepcoin/techniques/bb_reversal.py b/src/bithumb/techniques/bb_reversal.py
similarity index 94%
rename from src/deepcoin/techniques/bb_reversal.py
rename to src/bithumb/techniques/bb_reversal.py
index eb04469..15a793f 100644
--- a/src/deepcoin/techniques/bb_reversal.py
+++ b/src/bithumb/techniques/bb_reversal.py
@@ -4,8 +4,8 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.indicators import bollinger_bands, ema
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.indicators import bollinger_bands, ema
class BbReversalTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/bb_squeeze_breakout.py b/src/bithumb/techniques/bb_squeeze_breakout.py
similarity index 91%
rename from src/deepcoin/techniques/bb_squeeze_breakout.py
rename to src/bithumb/techniques/bb_squeeze_breakout.py
index ca87f61..7783a59 100644
--- a/src/deepcoin/techniques/bb_squeeze_breakout.py
+++ b/src/bithumb/techniques/bb_squeeze_breakout.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
-from deepcoin.techniques.indicators import bollinger_bands
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
+from bithumb.techniques.indicators import bollinger_bands
class BbSqueezeBreakoutTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/cci_extreme.py b/src/bithumb/techniques/cci_extreme.py
similarity index 90%
rename from src/deepcoin/techniques/cci_extreme.py
rename to src/bithumb/techniques/cci_extreme.py
index f4a34fd..53cf925 100644
--- a/src/deepcoin/techniques/cci_extreme.py
+++ b/src/bithumb/techniques/cci_extreme.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal, safe_float
-from deepcoin.techniques.indicators import cci
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal, safe_float
+from bithumb.techniques.indicators import cci
class CciExtremeTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/composite_base.py b/src/bithumb/techniques/composite_base.py
similarity index 97%
rename from src/deepcoin/techniques/composite_base.py
rename to src/bithumb/techniques/composite_base.py
index ac73714..22c2f31 100644
--- a/src/deepcoin/techniques/composite_base.py
+++ b/src/bithumb/techniques/composite_base.py
@@ -6,8 +6,8 @@ from dataclasses import dataclass
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.indicators import ema
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.indicators import ema
@dataclass(frozen=True)
diff --git a/src/deepcoin/techniques/composite_breakout.py b/src/bithumb/techniques/composite_breakout.py
similarity index 74%
rename from src/deepcoin/techniques/composite_breakout.py
rename to src/bithumb/techniques/composite_breakout.py
index 6c976db..ab2b01e 100644
--- a/src/deepcoin/techniques/composite_breakout.py
+++ b/src/bithumb/techniques/composite_breakout.py
@@ -4,18 +4,18 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.bb_squeeze_breakout import BbSqueezeBreakoutTechnique
-from deepcoin.techniques.composite_base import (
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.bb_squeeze_breakout import BbSqueezeBreakoutTechnique
+from bithumb.techniques.composite_base import (
cluster_events,
collect_weighted_events,
score_clusters_to_signals,
)
-from deepcoin.techniques.donchian import DonchianTechnique
-from deepcoin.techniques.keltner_breakout import KeltnerBreakoutTechnique
-from deepcoin.techniques.macd_cross import MacdCrossTechnique
-from deepcoin.techniques.range_breakout import RangeBreakoutTechnique
-from deepcoin.techniques.volume_breakout import VolumeBreakoutTechnique
+from bithumb.techniques.donchian import DonchianTechnique
+from bithumb.techniques.keltner_breakout import KeltnerBreakoutTechnique
+from bithumb.techniques.macd_cross import MacdCrossTechnique
+from bithumb.techniques.range_breakout import RangeBreakoutTechnique
+from bithumb.techniques.volume_breakout import VolumeBreakoutTechnique
_SUB = [
DonchianTechnique(),
diff --git a/src/deepcoin/techniques/composite_divergence.py b/src/bithumb/techniques/composite_divergence.py
similarity index 76%
rename from src/deepcoin/techniques/composite_divergence.py
rename to src/bithumb/techniques/composite_divergence.py
index 0912699..0fcb1de 100644
--- a/src/deepcoin/techniques/composite_divergence.py
+++ b/src/bithumb/techniques/composite_divergence.py
@@ -4,17 +4,17 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.composite_base import (
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.composite_base import (
cluster_events,
collect_weighted_events,
score_clusters_to_signals,
)
-from deepcoin.techniques.macd_cross import MacdCrossTechnique
-from deepcoin.techniques.macd_divergence import MacdDivergenceTechnique
-from deepcoin.techniques.obv_divergence import ObvDivergenceTechnique
-from deepcoin.techniques.rsi_divergence import RsiDivergenceTechnique
-from deepcoin.techniques.rsi_swing import RsiSwingTechnique
+from bithumb.techniques.macd_cross import MacdCrossTechnique
+from bithumb.techniques.macd_divergence import MacdDivergenceTechnique
+from bithumb.techniques.obv_divergence import ObvDivergenceTechnique
+from bithumb.techniques.rsi_divergence import RsiDivergenceTechnique
+from bithumb.techniques.rsi_swing import RsiSwingTechnique
_SUB = [
RsiDivergenceTechnique(),
diff --git a/src/deepcoin/techniques/composite_full.py b/src/bithumb/techniques/composite_full.py
similarity index 90%
rename from src/deepcoin/techniques/composite_full.py
rename to src/bithumb/techniques/composite_full.py
index b6bf021..63b15c4 100644
--- a/src/deepcoin/techniques/composite_full.py
+++ b/src/bithumb/techniques/composite_full.py
@@ -4,15 +4,15 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.composite_base import (
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.composite_base import (
cluster_events,
collect_weighted_events,
score_clusters_to_signals,
)
def _build_full_sub_techniques() -> list[BaseTechnique]:
"""복합 제외 단일 기법 목록을 반환한다."""
- from deepcoin.techniques.registry import get_single_techniques
+ from bithumb.techniques.registry import get_single_techniques
return get_single_techniques()
diff --git a/src/deepcoin/techniques/composite_pullback.py b/src/bithumb/techniques/composite_pullback.py
similarity index 74%
rename from src/deepcoin/techniques/composite_pullback.py
rename to src/bithumb/techniques/composite_pullback.py
index 4c0e286..67cf905 100644
--- a/src/deepcoin/techniques/composite_pullback.py
+++ b/src/bithumb/techniques/composite_pullback.py
@@ -4,18 +4,18 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.bb_reversal import BbReversalTechnique
-from deepcoin.techniques.composite_base import (
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.bb_reversal import BbReversalTechnique
+from bithumb.techniques.composite_base import (
cluster_events,
collect_weighted_events,
score_clusters_to_signals,
)
-from deepcoin.techniques.ema_pullback import EmaPullbackTechnique
-from deepcoin.techniques.fib_pullback import FibPullbackTechnique
-from deepcoin.techniques.keltner_reversal import KeltnerReversalTechnique
-from deepcoin.techniques.local_extrema import LocalExtremaTechnique
-from deepcoin.techniques.support_bounce import SupportBounceTechnique
+from bithumb.techniques.ema_pullback import EmaPullbackTechnique
+from bithumb.techniques.fib_pullback import FibPullbackTechnique
+from bithumb.techniques.keltner_reversal import KeltnerReversalTechnique
+from bithumb.techniques.local_extrema import LocalExtremaTechnique
+from bithumb.techniques.support_bounce import SupportBounceTechnique
_SUB = [
EmaPullbackTechnique(),
diff --git a/src/deepcoin/techniques/composite_swing.py b/src/bithumb/techniques/composite_swing.py
similarity index 73%
rename from src/deepcoin/techniques/composite_swing.py
rename to src/bithumb/techniques/composite_swing.py
index 53ba1c2..f606c34 100644
--- a/src/deepcoin/techniques/composite_swing.py
+++ b/src/bithumb/techniques/composite_swing.py
@@ -4,19 +4,19 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.composite_base import (
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.composite_base import (
cluster_events,
collect_weighted_events,
score_clusters_to_signals,
)
-from deepcoin.techniques.donchian import DonchianTechnique
-from deepcoin.techniques.fractal_swing import FractalSwingTechnique
-from deepcoin.techniques.local_extrema import LocalExtremaTechnique
-from deepcoin.techniques.minor_swing import MinorSwingTechnique
-from deepcoin.techniques.pivot_swing import PivotSwingTechnique
-from deepcoin.techniques.swing_failure import SwingFailureTechnique
-from deepcoin.techniques.zigzag_causal import ZigzagCausalTechnique
+from bithumb.techniques.donchian import DonchianTechnique
+from bithumb.techniques.fractal_swing import FractalSwingTechnique
+from bithumb.techniques.local_extrema import LocalExtremaTechnique
+from bithumb.techniques.minor_swing import MinorSwingTechnique
+from bithumb.techniques.pivot_swing import PivotSwingTechnique
+from bithumb.techniques.swing_failure import SwingFailureTechnique
+from bithumb.techniques.zigzag_causal import ZigzagCausalTechnique
_SUB = [
ZigzagCausalTechnique(),
diff --git a/src/deepcoin/techniques/composite_v3.py b/src/bithumb/techniques/composite_v3.py
similarity index 66%
rename from src/deepcoin/techniques/composite_v3.py
rename to src/bithumb/techniques/composite_v3.py
index ac9b29f..8d2e456 100644
--- a/src/deepcoin/techniques/composite_v3.py
+++ b/src/bithumb/techniques/composite_v3.py
@@ -4,29 +4,29 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.bb_reversal import BbReversalTechnique
-from deepcoin.techniques.composite_base import (
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.bb_reversal import BbReversalTechnique
+from bithumb.techniques.composite_base import (
cluster_events,
collect_weighted_events,
score_clusters_to_signals,
)
-from deepcoin.techniques.donchian import DonchianTechnique
-from deepcoin.techniques.ema_pullback import EmaPullbackTechnique
-from deepcoin.techniques.fib_pullback import FibPullbackTechnique
-from deepcoin.techniques.fractal_swing import FractalSwingTechnique
-from deepcoin.techniques.keltner_breakout import KeltnerBreakoutTechnique
-from deepcoin.techniques.local_extrema import LocalExtremaTechnique
-from deepcoin.techniques.macd_cross import MacdCrossTechnique
-from deepcoin.techniques.macd_divergence import MacdDivergenceTechnique
-from deepcoin.techniques.minor_swing import MinorSwingTechnique
-from deepcoin.techniques.obv_divergence import ObvDivergenceTechnique
-from deepcoin.techniques.pivot_swing import PivotSwingTechnique
-from deepcoin.techniques.range_breakout import RangeBreakoutTechnique
-from deepcoin.techniques.rsi_divergence import RsiDivergenceTechnique
-from deepcoin.techniques.rsi_swing import RsiSwingTechnique
-from deepcoin.techniques.support_bounce import SupportBounceTechnique
-from deepcoin.techniques.zigzag_causal import ZigzagCausalTechnique
+from bithumb.techniques.donchian import DonchianTechnique
+from bithumb.techniques.ema_pullback import EmaPullbackTechnique
+from bithumb.techniques.fib_pullback import FibPullbackTechnique
+from bithumb.techniques.fractal_swing import FractalSwingTechnique
+from bithumb.techniques.keltner_breakout import KeltnerBreakoutTechnique
+from bithumb.techniques.local_extrema import LocalExtremaTechnique
+from bithumb.techniques.macd_cross import MacdCrossTechnique
+from bithumb.techniques.macd_divergence import MacdDivergenceTechnique
+from bithumb.techniques.minor_swing import MinorSwingTechnique
+from bithumb.techniques.obv_divergence import ObvDivergenceTechnique
+from bithumb.techniques.pivot_swing import PivotSwingTechnique
+from bithumb.techniques.range_breakout import RangeBreakoutTechnique
+from bithumb.techniques.rsi_divergence import RsiDivergenceTechnique
+from bithumb.techniques.rsi_swing import RsiSwingTechnique
+from bithumb.techniques.support_bounce import SupportBounceTechnique
+from bithumb.techniques.zigzag_causal import ZigzagCausalTechnique
_TECHNIQUE_WEIGHTS: dict[str, tuple[float, float]] = {
"zigzag_causal": (2.5, 2.5),
diff --git a/src/deepcoin/techniques/donchian.py b/src/bithumb/techniques/donchian.py
similarity index 96%
rename from src/deepcoin/techniques/donchian.py
rename to src/bithumb/techniques/donchian.py
index 772f5a6..438b71c 100644
--- a/src/deepcoin/techniques/donchian.py
+++ b/src/bithumb/techniques/donchian.py
@@ -4,7 +4,7 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
class DonchianTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/ema_pullback.py b/src/bithumb/techniques/ema_pullback.py
similarity index 92%
rename from src/deepcoin/techniques/ema_pullback.py
rename to src/bithumb/techniques/ema_pullback.py
index 8647a19..c4e9ca7 100644
--- a/src/deepcoin/techniques/ema_pullback.py
+++ b/src/bithumb/techniques/ema_pullback.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
-from deepcoin.techniques.indicators import ema
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
+from bithumb.techniques.indicators import ema
class EmaPullbackTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/fib_pullback.py b/src/bithumb/techniques/fib_pullback.py
similarity index 95%
rename from src/deepcoin/techniques/fib_pullback.py
rename to src/bithumb/techniques/fib_pullback.py
index 707b10c..818126c 100644
--- a/src/deepcoin/techniques/fib_pullback.py
+++ b/src/bithumb/techniques/fib_pullback.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import find_confirmed_pivots, make_signal
-from deepcoin.techniques.indicators import ema
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import find_confirmed_pivots, make_signal
+from bithumb.techniques.indicators import ema
class FibPullbackTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/fractal_swing.py b/src/bithumb/techniques/fractal_swing.py
similarity index 90%
rename from src/deepcoin/techniques/fractal_swing.py
rename to src/bithumb/techniques/fractal_swing.py
index 6ae0928..a2b6e77 100644
--- a/src/deepcoin/techniques/fractal_swing.py
+++ b/src/bithumb/techniques/fractal_swing.py
@@ -4,8 +4,8 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import dedupe_signals, find_fractal_pivots, make_signal
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import dedupe_signals, find_fractal_pivots, make_signal
class FractalSwingTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/helpers.py b/src/bithumb/techniques/helpers.py
similarity index 99%
rename from src/deepcoin/techniques/helpers.py
rename to src/bithumb/techniques/helpers.py
index ef0e9b1..994943f 100644
--- a/src/deepcoin/techniques/helpers.py
+++ b/src/bithumb/techniques/helpers.py
@@ -4,7 +4,7 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import TechniqueSignal
+from bithumb.techniques.base import TechniqueSignal
def safe_float(value: object) -> float | None:
diff --git a/src/deepcoin/techniques/ichimoku_trend.py b/src/bithumb/techniques/ichimoku_trend.py
similarity index 89%
rename from src/deepcoin/techniques/ichimoku_trend.py
rename to src/bithumb/techniques/ichimoku_trend.py
index f45f6da..6bf8d46 100644
--- a/src/deepcoin/techniques/ichimoku_trend.py
+++ b/src/bithumb/techniques/ichimoku_trend.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
-from deepcoin.techniques.indicators import ichimoku
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
+from bithumb.techniques.indicators import ichimoku
class IchimokuTrendTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/indicators.py b/src/bithumb/techniques/indicators.py
similarity index 100%
rename from src/deepcoin/techniques/indicators.py
rename to src/bithumb/techniques/indicators.py
diff --git a/src/deepcoin/techniques/keltner_breakout.py b/src/bithumb/techniques/keltner_breakout.py
similarity index 90%
rename from src/deepcoin/techniques/keltner_breakout.py
rename to src/bithumb/techniques/keltner_breakout.py
index 24240bf..042c85d 100644
--- a/src/deepcoin/techniques/keltner_breakout.py
+++ b/src/bithumb/techniques/keltner_breakout.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
-from deepcoin.techniques.indicators import keltner_channels
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
+from bithumb.techniques.indicators import keltner_channels
class KeltnerBreakoutTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/keltner_reversal.py b/src/bithumb/techniques/keltner_reversal.py
similarity index 91%
rename from src/deepcoin/techniques/keltner_reversal.py
rename to src/bithumb/techniques/keltner_reversal.py
index 2a17975..9a3a464 100644
--- a/src/deepcoin/techniques/keltner_reversal.py
+++ b/src/bithumb/techniques/keltner_reversal.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
-from deepcoin.techniques.indicators import keltner_channels
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
+from bithumb.techniques.indicators import keltner_channels
class KeltnerReversalTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/legs.py b/src/bithumb/techniques/legs.py
similarity index 98%
rename from src/deepcoin/techniques/legs.py
rename to src/bithumb/techniques/legs.py
index 5adb1b1..47c5bd5 100644
--- a/src/deepcoin/techniques/legs.py
+++ b/src/bithumb/techniques/legs.py
@@ -4,7 +4,7 @@ from __future__ import annotations
from typing import Any
-from deepcoin.techniques.base import TechniqueSignal
+from bithumb.techniques.base import TechniqueSignal
def signals_to_legs(
diff --git a/src/deepcoin/techniques/local_extrema.py b/src/bithumb/techniques/local_extrema.py
similarity index 98%
rename from src/deepcoin/techniques/local_extrema.py
rename to src/bithumb/techniques/local_extrema.py
index 5b79d26..59c2eb8 100644
--- a/src/deepcoin/techniques/local_extrema.py
+++ b/src/bithumb/techniques/local_extrema.py
@@ -4,7 +4,7 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
class LocalExtremaTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/ma_cross.py b/src/bithumb/techniques/ma_cross.py
similarity index 94%
rename from src/deepcoin/techniques/ma_cross.py
rename to src/bithumb/techniques/ma_cross.py
index 1c2bb27..a6cf005 100644
--- a/src/deepcoin/techniques/ma_cross.py
+++ b/src/bithumb/techniques/ma_cross.py
@@ -4,8 +4,8 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.indicators import ema
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.indicators import ema
class MaCrossTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/macd_cross.py b/src/bithumb/techniques/macd_cross.py
similarity index 94%
rename from src/deepcoin/techniques/macd_cross.py
rename to src/bithumb/techniques/macd_cross.py
index 366ae4c..dce558f 100644
--- a/src/deepcoin/techniques/macd_cross.py
+++ b/src/bithumb/techniques/macd_cross.py
@@ -4,8 +4,8 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.indicators import macd
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.indicators import macd
class MacdCrossTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/macd_divergence.py b/src/bithumb/techniques/macd_divergence.py
similarity index 93%
rename from src/deepcoin/techniques/macd_divergence.py
rename to src/bithumb/techniques/macd_divergence.py
index 7fd2416..89aa232 100644
--- a/src/deepcoin/techniques/macd_divergence.py
+++ b/src/bithumb/techniques/macd_divergence.py
@@ -4,15 +4,15 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import (
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import (
dedupe_signals,
detect_bearish_divergence,
detect_bullish_divergence,
find_confirmed_pivots,
make_signal,
)
-from deepcoin.techniques.indicators import macd
+from bithumb.techniques.indicators import macd
class MacdDivergenceTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/minor_swing.py b/src/bithumb/techniques/minor_swing.py
similarity index 91%
rename from src/deepcoin/techniques/minor_swing.py
rename to src/bithumb/techniques/minor_swing.py
index c4525e3..16f7322 100644
--- a/src/deepcoin/techniques/minor_swing.py
+++ b/src/bithumb/techniques/minor_swing.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.local_extrema import LocalExtremaTechnique
-from deepcoin.techniques.zigzag_causal import find_causal_zigzag_signals
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.local_extrema import LocalExtremaTechnique
+from bithumb.techniques.zigzag_causal import find_causal_zigzag_signals
class MinorSwingTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/obv_divergence.py b/src/bithumb/techniques/obv_divergence.py
similarity index 93%
rename from src/deepcoin/techniques/obv_divergence.py
rename to src/bithumb/techniques/obv_divergence.py
index bb9b9f4..1e74640 100644
--- a/src/deepcoin/techniques/obv_divergence.py
+++ b/src/bithumb/techniques/obv_divergence.py
@@ -4,15 +4,15 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import (
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import (
dedupe_signals,
detect_bearish_divergence,
detect_bullish_divergence,
find_confirmed_pivots,
make_signal,
)
-from deepcoin.techniques.indicators import obv
+from bithumb.techniques.indicators import obv
class ObvDivergenceTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/parabolic_sar_signal.py b/src/bithumb/techniques/parabolic_sar_signal.py
similarity index 87%
rename from src/deepcoin/techniques/parabolic_sar_signal.py
rename to src/bithumb/techniques/parabolic_sar_signal.py
index 8570988..88fabb0 100644
--- a/src/deepcoin/techniques/parabolic_sar_signal.py
+++ b/src/bithumb/techniques/parabolic_sar_signal.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
-from deepcoin.techniques.indicators import parabolic_sar
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
+from bithumb.techniques.indicators import parabolic_sar
class ParabolicSarTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/pivot_points.py b/src/bithumb/techniques/pivot_points.py
similarity index 90%
rename from src/deepcoin/techniques/pivot_points.py
rename to src/bithumb/techniques/pivot_points.py
index 0d78150..815ed3e 100644
--- a/src/deepcoin/techniques/pivot_points.py
+++ b/src/bithumb/techniques/pivot_points.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
-from deepcoin.techniques.indicators import rolling_pivot_points
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
+from bithumb.techniques.indicators import rolling_pivot_points
class PivotPointsTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/pivot_swing.py b/src/bithumb/techniques/pivot_swing.py
similarity index 90%
rename from src/deepcoin/techniques/pivot_swing.py
rename to src/bithumb/techniques/pivot_swing.py
index ba5fdba..f4e56f2 100644
--- a/src/deepcoin/techniques/pivot_swing.py
+++ b/src/bithumb/techniques/pivot_swing.py
@@ -4,8 +4,8 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import dedupe_signals, find_confirmed_pivots, make_signal
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import dedupe_signals, find_confirmed_pivots, make_signal
class PivotSwingTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/range_breakout.py b/src/bithumb/techniques/range_breakout.py
similarity index 92%
rename from src/deepcoin/techniques/range_breakout.py
rename to src/bithumb/techniques/range_breakout.py
index d028ce1..e931a30 100644
--- a/src/deepcoin/techniques/range_breakout.py
+++ b/src/bithumb/techniques/range_breakout.py
@@ -4,8 +4,8 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
class RangeBreakoutTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/registry.py b/src/bithumb/techniques/registry.py
similarity index 50%
rename from src/deepcoin/techniques/registry.py
rename to src/bithumb/techniques/registry.py
index c28fc29..28ccaf7 100644
--- a/src/deepcoin/techniques/registry.py
+++ b/src/bithumb/techniques/registry.py
@@ -2,46 +2,46 @@
from __future__ import annotations
-from deepcoin.techniques.base import BaseTechnique
-from deepcoin.techniques.adx_trend import AdxTrendTechnique
-from deepcoin.techniques.atr_channel import AtrChannelTechnique
-from deepcoin.techniques.bb_reversal import BbReversalTechnique
-from deepcoin.techniques.bb_squeeze_breakout import BbSqueezeBreakoutTechnique
-from deepcoin.techniques.cci_extreme import CciExtremeTechnique
-from deepcoin.techniques.composite_breakout import CompositeBreakoutTechnique
-from deepcoin.techniques.composite_divergence import CompositeDivergenceTechnique
-from deepcoin.techniques.composite_full import CompositeFullTechnique
-from deepcoin.techniques.composite_pullback import CompositePullbackTechnique
-from deepcoin.techniques.composite_swing import CompositeSwingTechnique
-from deepcoin.techniques.composite_v3 import CompositeV3Technique
-from deepcoin.techniques.donchian import DonchianTechnique
-from deepcoin.techniques.ema_pullback import EmaPullbackTechnique
-from deepcoin.techniques.fib_pullback import FibPullbackTechnique
-from deepcoin.techniques.fractal_swing import FractalSwingTechnique
-from deepcoin.techniques.ichimoku_trend import IchimokuTrendTechnique
-from deepcoin.techniques.keltner_breakout import KeltnerBreakoutTechnique
-from deepcoin.techniques.keltner_reversal import KeltnerReversalTechnique
-from deepcoin.techniques.local_extrema import LocalExtremaTechnique
-from deepcoin.techniques.ma_cross import MaCrossTechnique
-from deepcoin.techniques.macd_cross import MacdCrossTechnique
-from deepcoin.techniques.macd_divergence import MacdDivergenceTechnique
-from deepcoin.techniques.minor_swing import MinorSwingTechnique
-from deepcoin.techniques.obv_divergence import ObvDivergenceTechnique
-from deepcoin.techniques.parabolic_sar_signal import ParabolicSarTechnique
-from deepcoin.techniques.pivot_points import PivotPointsTechnique
-from deepcoin.techniques.pivot_swing import PivotSwingTechnique
-from deepcoin.techniques.range_breakout import RangeBreakoutTechnique
-from deepcoin.techniques.roc_reversal import RocReversalTechnique
-from deepcoin.techniques.rsi_divergence import RsiDivergenceTechnique
-from deepcoin.techniques.rsi_swing import RsiSwingTechnique
-from deepcoin.techniques.stochastic_cross import StochasticCrossTechnique
-from deepcoin.techniques.supertrend_signal import SupertrendTechnique
-from deepcoin.techniques.support_bounce import SupportBounceTechnique
-from deepcoin.techniques.support_resistance import SupportResistanceTechnique
-from deepcoin.techniques.swing_failure import SwingFailureTechnique
-from deepcoin.techniques.volume_breakout import VolumeBreakoutTechnique
-from deepcoin.techniques.volume_spike import VolumeSpikeTechnique
-from deepcoin.techniques.zigzag_causal import ZigzagCausalTechnique
+from bithumb.techniques.base import BaseTechnique
+from bithumb.techniques.adx_trend import AdxTrendTechnique
+from bithumb.techniques.atr_channel import AtrChannelTechnique
+from bithumb.techniques.bb_reversal import BbReversalTechnique
+from bithumb.techniques.bb_squeeze_breakout import BbSqueezeBreakoutTechnique
+from bithumb.techniques.cci_extreme import CciExtremeTechnique
+from bithumb.techniques.composite_breakout import CompositeBreakoutTechnique
+from bithumb.techniques.composite_divergence import CompositeDivergenceTechnique
+from bithumb.techniques.composite_full import CompositeFullTechnique
+from bithumb.techniques.composite_pullback import CompositePullbackTechnique
+from bithumb.techniques.composite_swing import CompositeSwingTechnique
+from bithumb.techniques.composite_v3 import CompositeV3Technique
+from bithumb.techniques.donchian import DonchianTechnique
+from bithumb.techniques.ema_pullback import EmaPullbackTechnique
+from bithumb.techniques.fib_pullback import FibPullbackTechnique
+from bithumb.techniques.fractal_swing import FractalSwingTechnique
+from bithumb.techniques.ichimoku_trend import IchimokuTrendTechnique
+from bithumb.techniques.keltner_breakout import KeltnerBreakoutTechnique
+from bithumb.techniques.keltner_reversal import KeltnerReversalTechnique
+from bithumb.techniques.local_extrema import LocalExtremaTechnique
+from bithumb.techniques.ma_cross import MaCrossTechnique
+from bithumb.techniques.macd_cross import MacdCrossTechnique
+from bithumb.techniques.macd_divergence import MacdDivergenceTechnique
+from bithumb.techniques.minor_swing import MinorSwingTechnique
+from bithumb.techniques.obv_divergence import ObvDivergenceTechnique
+from bithumb.techniques.parabolic_sar_signal import ParabolicSarTechnique
+from bithumb.techniques.pivot_points import PivotPointsTechnique
+from bithumb.techniques.pivot_swing import PivotSwingTechnique
+from bithumb.techniques.range_breakout import RangeBreakoutTechnique
+from bithumb.techniques.roc_reversal import RocReversalTechnique
+from bithumb.techniques.rsi_divergence import RsiDivergenceTechnique
+from bithumb.techniques.rsi_swing import RsiSwingTechnique
+from bithumb.techniques.stochastic_cross import StochasticCrossTechnique
+from bithumb.techniques.supertrend_signal import SupertrendTechnique
+from bithumb.techniques.support_bounce import SupportBounceTechnique
+from bithumb.techniques.support_resistance import SupportResistanceTechnique
+from bithumb.techniques.swing_failure import SwingFailureTechnique
+from bithumb.techniques.volume_breakout import VolumeBreakoutTechnique
+from bithumb.techniques.volume_spike import VolumeSpikeTechnique
+from bithumb.techniques.zigzag_causal import ZigzagCausalTechnique
# 카테고리별 단일 기법 (인과, 미래 데이터 미사용)
_SINGLE_TECHNIQUES: list[BaseTechnique] = [
diff --git a/src/deepcoin/techniques/roc_reversal.py b/src/bithumb/techniques/roc_reversal.py
similarity index 89%
rename from src/deepcoin/techniques/roc_reversal.py
rename to src/bithumb/techniques/roc_reversal.py
index d13963a..be41c2c 100644
--- a/src/deepcoin/techniques/roc_reversal.py
+++ b/src/bithumb/techniques/roc_reversal.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal, safe_float
-from deepcoin.techniques.indicators import roc
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal, safe_float
+from bithumb.techniques.indicators import roc
class RocReversalTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/rsi_divergence.py b/src/bithumb/techniques/rsi_divergence.py
similarity index 93%
rename from src/deepcoin/techniques/rsi_divergence.py
rename to src/bithumb/techniques/rsi_divergence.py
index 3def391..329c778 100644
--- a/src/deepcoin/techniques/rsi_divergence.py
+++ b/src/bithumb/techniques/rsi_divergence.py
@@ -4,15 +4,15 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import (
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import (
dedupe_signals,
detect_bearish_divergence,
detect_bullish_divergence,
find_confirmed_pivots,
make_signal,
)
-from deepcoin.techniques.indicators import rsi
+from bithumb.techniques.indicators import rsi
class RsiDivergenceTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/rsi_swing.py b/src/bithumb/techniques/rsi_swing.py
similarity index 94%
rename from src/deepcoin/techniques/rsi_swing.py
rename to src/bithumb/techniques/rsi_swing.py
index acdb552..170c47f 100644
--- a/src/deepcoin/techniques/rsi_swing.py
+++ b/src/bithumb/techniques/rsi_swing.py
@@ -4,8 +4,8 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.indicators import rsi
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.indicators import rsi
class RsiSwingTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/runner.py b/src/bithumb/techniques/runner.py
similarity index 92%
rename from src/deepcoin/techniques/runner.py
rename to src/bithumb/techniques/runner.py
index 172b023..2331314 100644
--- a/src/deepcoin/techniques/runner.py
+++ b/src/bithumb/techniques/runner.py
@@ -13,13 +13,13 @@ import pandas as pd
logger = logging.getLogger(__name__)
-from deepcoin.data.candle_loader import load_candles
-from deepcoin.data.intervals import interval_label
-from deepcoin.evaluation.gt_align import align_with_ground_truth
-from deepcoin.ground_truth.pnl import simulate_gt_pnl
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueResult
-from deepcoin.techniques.legs import legs_to_signal_dicts, signals_to_legs, summarize_legs
-from deepcoin.techniques.registry import get_all_techniques, get_technique
+from bithumb.data.candle_loader import load_candles
+from bithumb.data.intervals import interval_label
+from bithumb.evaluation.gt_align import align_with_ground_truth
+from bithumb.ground_truth.pnl import simulate_gt_pnl
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueResult
+from bithumb.techniques.legs import legs_to_signal_dicts, signals_to_legs, summarize_legs
+from bithumb.techniques.registry import get_all_techniques, get_technique
def run_technique(
diff --git a/src/deepcoin/techniques/stochastic_cross.py b/src/bithumb/techniques/stochastic_cross.py
similarity index 90%
rename from src/deepcoin/techniques/stochastic_cross.py
rename to src/bithumb/techniques/stochastic_cross.py
index cd9f9fb..44f0925 100644
--- a/src/deepcoin/techniques/stochastic_cross.py
+++ b/src/bithumb/techniques/stochastic_cross.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
-from deepcoin.techniques.indicators import stochastic
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
+from bithumb.techniques.indicators import stochastic
class StochasticCrossTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/supertrend_signal.py b/src/bithumb/techniques/supertrend_signal.py
similarity index 88%
rename from src/deepcoin/techniques/supertrend_signal.py
rename to src/bithumb/techniques/supertrend_signal.py
index c2914c3..0a61fbc 100644
--- a/src/deepcoin/techniques/supertrend_signal.py
+++ b/src/bithumb/techniques/supertrend_signal.py
@@ -4,9 +4,9 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
-from deepcoin.techniques.indicators import supertrend
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
+from bithumb.techniques.indicators import supertrend
class SupertrendTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/support_bounce.py b/src/bithumb/techniques/support_bounce.py
similarity index 93%
rename from src/deepcoin/techniques/support_bounce.py
rename to src/bithumb/techniques/support_bounce.py
index 9cdb532..c4bbed4 100644
--- a/src/deepcoin/techniques/support_bounce.py
+++ b/src/bithumb/techniques/support_bounce.py
@@ -4,8 +4,8 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
class SupportBounceTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/support_resistance.py b/src/bithumb/techniques/support_resistance.py
similarity index 93%
rename from src/deepcoin/techniques/support_resistance.py
rename to src/bithumb/techniques/support_resistance.py
index c63cba7..250ca09 100644
--- a/src/deepcoin/techniques/support_resistance.py
+++ b/src/bithumb/techniques/support_resistance.py
@@ -4,8 +4,8 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import dedupe_signals, find_confirmed_pivots, make_signal
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import dedupe_signals, find_confirmed_pivots, make_signal
class SupportResistanceTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/swing_failure.py b/src/bithumb/techniques/swing_failure.py
similarity index 93%
rename from src/deepcoin/techniques/swing_failure.py
rename to src/bithumb/techniques/swing_failure.py
index 1b75a82..e75a9f5 100644
--- a/src/deepcoin/techniques/swing_failure.py
+++ b/src/bithumb/techniques/swing_failure.py
@@ -4,8 +4,8 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import dedupe_signals, find_confirmed_pivots, make_signal
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import dedupe_signals, find_confirmed_pivots, make_signal
class SwingFailureTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/volume_breakout.py b/src/bithumb/techniques/volume_breakout.py
similarity index 92%
rename from src/deepcoin/techniques/volume_breakout.py
rename to src/bithumb/techniques/volume_breakout.py
index 15349b5..bc865bb 100644
--- a/src/deepcoin/techniques/volume_breakout.py
+++ b/src/bithumb/techniques/volume_breakout.py
@@ -4,8 +4,8 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
class VolumeBreakoutTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/volume_spike.py b/src/bithumb/techniques/volume_spike.py
similarity index 93%
rename from src/deepcoin/techniques/volume_spike.py
rename to src/bithumb/techniques/volume_spike.py
index 4307311..88e25bb 100644
--- a/src/deepcoin/techniques/volume_spike.py
+++ b/src/bithumb/techniques/volume_spike.py
@@ -4,8 +4,8 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
-from deepcoin.techniques.helpers import make_signal
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.helpers import make_signal
class VolumeSpikeTechnique(BaseTechnique):
diff --git a/src/deepcoin/techniques/zigzag_causal.py b/src/bithumb/techniques/zigzag_causal.py
similarity index 97%
rename from src/deepcoin/techniques/zigzag_causal.py
rename to src/bithumb/techniques/zigzag_causal.py
index 352add9..780f317 100644
--- a/src/deepcoin/techniques/zigzag_causal.py
+++ b/src/bithumb/techniques/zigzag_causal.py
@@ -4,7 +4,7 @@ from __future__ import annotations
import pandas as pd
-from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
+from bithumb.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
class ZigzagCausalTechnique(BaseTechnique):
diff --git a/src/deepcoin/__init__.py b/src/deepcoin/__init__.py
deleted file mode 100644
index 8f97be6..0000000
--- a/src/deepcoin/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-"""DeepCoin — 빗썸 암호화폐 데이터 수집·분석."""
-
-__version__ = "0.1.0"
diff --git a/src/deepcoin/ground_truth/futures.py b/src/deepcoin/ground_truth/futures.py
deleted file mode 100644
index b08e098..0000000
--- a/src/deepcoin/ground_truth/futures.py
+++ /dev/null
@@ -1,128 +0,0 @@
-"""현물 Ground Truth 신호를 선물 롱·숏 타점으로 변환한다."""
-
-from __future__ import annotations
-
-from typing import Any
-
-import pandas as pd
-
-
-def futures_events_from_gt_signals(
- signals: list[dict[str, Any]],
-) -> list[dict[str, Any]]:
- """현물 GT 신호를 선물 이벤트 스트림으로 변환한다.
-
- 동일 봉에서 청산 이벤트를 먼저, 진입 이벤트를 나중에 배치한다.
-
- Args:
- signals: GT signals 리스트.
-
- Returns:
- position, action, event_order 등이 포함된 이벤트 리스트.
- """
- events: list[dict[str, Any]] = []
- for sig in signals:
- side = sig["side"]
- signal_type = sig.get(
- "signal_type",
- "swing_low" if side == "buy" else "swing_high",
- )
- base = {
- "datetime": sig["datetime"],
- "price": sig["price"],
- "bar_index": sig.get("bar_index", 0),
- "marker_id": sig.get("marker_id", sig.get("leg_id")),
- "signal_type": signal_type,
- "leg_id": sig.get("leg_id"),
- }
- if side == "buy":
- events.append({**base, "position": "short", "action": "close", "event_order": 0})
- events.append({**base, "position": "long", "action": "open", "event_order": 1})
- else:
- events.append({**base, "position": "long", "action": "close", "event_order": 0})
- events.append({**base, "position": "short", "action": "open", "event_order": 1})
- return events
-
-
-def futures_markers_from_gt_signals(
- gt_result: dict[str, Any],
-) -> dict[str, list[dict[str, Any]]]:
- """현물 GT 신호를 선물 4종 마커(상·하방 매수·매도)로 변환한다.
-
- 현물 GT의 스윙 저점(buy)과 고점(sell)을 동일 가격·시각에
- 롱·숏 양방향 선물 타점으로 매핑한다.
-
- - buy → 상방 매수(롱 진입), 하방 매도(숏 청산)
- - sell → 상방 매도(롱 청산), 하방 매수(숏 진입)
-
- Args:
- gt_result: build_ground_truth 결과 또는 동일 스키마 JSON.
-
- Returns:
- long_open, long_close, short_open, short_close 마커 리스트.
- """
- long_open: list[dict[str, Any]] = []
- long_close: list[dict[str, Any]] = []
- short_open: list[dict[str, Any]] = []
- short_close: list[dict[str, Any]] = []
-
- for sig in gt_result.get("signals") or []:
- side = sig["side"]
- signal_type = sig.get(
- "signal_type",
- "swing_low" if side == "buy" else "swing_high",
- )
- marker_id = sig.get("marker_id", sig.get("leg_id"))
- base = {
- "time": int(pd.Timestamp(sig["datetime"]).timestamp()),
- "price": sig["price"],
- "marker_id": marker_id,
- "signal_type": signal_type,
- "leg_id": sig.get("leg_id"),
- }
- if side == "buy":
- long_open.append({**base, "position": "long", "action": "open"})
- short_close.append({**base, "position": "short", "action": "close"})
- else:
- long_close.append({**base, "position": "long", "action": "close"})
- short_open.append({**base, "position": "short", "action": "open"})
-
- return {
- "long_open": long_open,
- "long_close": long_close,
- "short_open": short_open,
- "short_close": short_close,
- }
-
-
-def futures_markers_from_executed_trades(
- sim_pnl: dict[str, Any],
-) -> dict[str, list[dict[str, Any]]]:
- """시뮬 체결 내역에서 4종 선물 마커를 구성한다."""
- markers: dict[str, list[dict[str, Any]]] = {
- "long_open": [],
- "long_close": [],
- "short_open": [],
- "short_close": [],
- }
-
- for trade in sim_pnl.get("trades") or []:
- if trade.get("skipped"):
- continue
- position = trade["position"]
- action = trade["action"]
- key = f"{position}_{action}"
- if key not in ("long_open", "long_close", "short_open", "short_close"):
- continue
- markers[key].append(
- {
- "time": int(pd.Timestamp(trade["datetime"]).timestamp()),
- "price": trade["price"],
- "marker_id": trade.get("marker_id") or trade.get("trade_id"),
- "signal_type": trade.get("signal_type", ""),
- "position": position,
- "action": action,
- }
- )
-
- return markers
diff --git a/src/deepcoin/ground_truth/futures_chart.py b/src/deepcoin/ground_truth/futures_chart.py
deleted file mode 100644
index 1dcc961..0000000
--- a/src/deepcoin/ground_truth/futures_chart.py
+++ /dev/null
@@ -1,763 +0,0 @@
-"""선물 Ground Truth 차트 HTML 생성 (롱·숏 4색 마커)."""
-
-from __future__ import annotations
-
-import json
-from pathlib import Path
-from typing import Any
-
-from deepcoin.data.candle_loader import load_candles
-from deepcoin.ground_truth.chart import (
- DEFAULT_MAX_CANDLES,
- _data_js_path,
- _enrich_markers_chart_price,
- _sim_start_marker,
- _to_unix_seconds,
-)
-from deepcoin.ground_truth.futures import futures_markers_from_gt_signals
-
-
-def _stack_marker_positions(
- long_open: list[dict[str, Any]],
- long_close: list[dict[str, Any]],
- short_open: list[dict[str, Any]],
- short_close: list[dict[str, Any]],
-) -> None:
- """동일 시각에 겹치는 마커에 세로 스택 인덱스를 부여한다.
-
- 같은 봉의 L↓·S↓ 등이 좌우로 벌어지지 않고 동일 X에서 위아래로 쌓이도록 한다.
- """
- from collections import defaultdict
-
- groups: dict[int, list[dict[str, Any]]] = defaultdict(list)
- for markers in (long_open, long_close, short_open, short_close):
- for marker in markers:
- groups[int(marker["time"])].append(marker)
-
- kind_rank = {
- "long_close": 0,
- "short_open": 1,
- "long_open": 2,
- "short_close": 3,
- }
-
- for markers_at_time in groups.values():
- if len(markers_at_time) <= 1:
- for marker in markers_at_time:
- marker["stack_index"] = 0
- continue
- markers_at_time.sort(
- key=lambda m: kind_rank.get(f"{m.get('position')}_{m.get('action')}", 9)
- )
- for index, marker in enumerate(markers_at_time):
- marker["stack_index"] = index
- marker.pop("x_offset_px", None)
-
-_FUTURES_HTML_TEMPLATE = """
-
-
-
-
Futures Ground Truth Chart
-
-
-
-
-
-
-
-
- Futures Ground Truth Chart
-
-
-
-__EXTRA_BODY__
-
-
-
-
-
-"""
-
-
-def _build_futures_chart_payload(
- df,
- gt_result: dict[str, Any],
- chart_days: int,
- gt_lookback_days: int,
-) -> dict[str, Any]:
- """선물 GT 차트용 JSON payload를 구성한다."""
- markers = futures_markers_from_gt_signals(gt_result)
- times = _to_unix_seconds(df["datetime"])
- closes = df["close"].astype(float).tolist()
- chart_meta = {
- **gt_result["meta"],
- "market_type": "futures",
- "chart_lookback_days": chart_days,
- "gt_lookback_days": gt_lookback_days,
- "chart_data_from": str(df["datetime"].min()),
- "chart_data_to": str(df["datetime"].max()),
- }
- payload: dict[str, Any] = {
- "times": times,
- "open": df["open"].astype(float).tolist(),
- "high": df["high"].astype(float).tolist(),
- "low": df["low"].astype(float).tolist(),
- "close": closes,
- "long_open_markers": _enrich_markers_chart_price(markers["long_open"], times, closes),
- "long_close_markers": _enrich_markers_chart_price(markers["long_close"], times, closes),
- "short_open_markers": _enrich_markers_chart_price(markers["short_open"], times, closes),
- "short_close_markers": _enrich_markers_chart_price(markers["short_close"], times, closes),
- "meta": chart_meta,
- "bar_count": len(df),
- }
- _stack_marker_positions(
- payload["long_open_markers"],
- payload["long_close_markers"],
- payload["short_open_markers"],
- payload["short_close_markers"],
- )
- return payload
-
-
-def render_futures_ground_truth_chart(
- db_path: Path,
- symbol: str,
- gt_result: dict[str, Any],
- output_path: Path,
- chart_lookback_days: int | None = None,
- max_candles: int = DEFAULT_MAX_CANDLES,
-) -> Path:
- """현물 GT 타점을 선물 롱·숏 4색 마커로 표시한 HTML 차트를 생성한다.
-
- Args:
- db_path: SQLite 경로.
- symbol: 코인 심볼.
- gt_result: build_ground_truth 결과 또는 spot GT JSON.
- output_path: HTML 출력 경로.
- chart_lookback_days: 차트 표시 일수. None이면 GT lookback과 동일.
- max_candles: 0이면 전체, 양수면 최근 N봉만.
-
- Returns:
- HTML 저장 경로.
- """
- interval_min = gt_result["meta"]["interval_min"]
- gt_lookback_days = gt_result["meta"]["lookback_days"]
- chart_days = chart_lookback_days if chart_lookback_days is not None else gt_lookback_days
-
- df = load_candles(db_path, symbol, interval_min, lookback_days=chart_days)
- if max_candles > 0 and len(df) > max_candles:
- df = df.iloc[-max_candles:].reset_index(drop=True)
-
- payload = _build_futures_chart_payload(df, gt_result, chart_days, gt_lookback_days)
-
- output_path.parent.mkdir(parents=True, exist_ok=True)
- data_path = _data_js_path(output_path)
- with data_path.open("w", encoding="utf-8") as fp:
- fp.write("window.CHART_DATA=")
- json.dump(payload, fp, ensure_ascii=False, separators=(",", ":"))
- fp.write(";")
-
- data_js_name = data_path.name
- html = _futures_html_template(data_js_name)
- output_path.write_text(html, encoding="utf-8")
- return output_path
-
-
-def _futures_html_template(data_js_name: str) -> str:
- """선물 GT 차트 HTML 템플릿을 생성한다."""
- return (
- _FUTURES_HTML_TEMPLATE.replace("__DATA_JS_NAME__", data_js_name)
- .replace("__EXTRA_STYLES__", "")
- .replace("__EXTRA_BODY__", "")
- .replace("__EXTRA_SCRIPT__", "")
- )
diff --git a/src/deepcoin/ground_truth/order_sizing.py b/src/deepcoin/ground_truth/order_sizing.py
deleted file mode 100644
index 0e07968..0000000
--- a/src/deepcoin/ground_truth/order_sizing.py
+++ /dev/null
@@ -1,59 +0,0 @@
-"""총평가금액 구간별 매수(현금) 상한."""
-
-from __future__ import annotations
-
-from typing import Any
-
-# 총평가금액(원) 구간 — 높은 구간이 우선 적용
-EQUITY_TIER_100M = 100_000_000
-EQUITY_TIER_1B = 1_000_000_000
-EQUITY_TIER_10B = 10_000_000_000
-
-BUY_SIZING_RULE_LABEL = "총평가 1억↑ 현금 10% · 10억↑ 5% · 100억↑ 1%"
-
-
-def buy_cash_pct(equity_krw: float) -> float | None:
- """총평가금액에 따른 1회 매수 허용 현금 비율.
-
- Args:
- equity_krw: 현재 총평가금액(원).
-
- Returns:
- 허용 비율(0~1). 1억 미만이면 None(비율 상한 없음).
- """
- if equity_krw >= EQUITY_TIER_10B:
- return 0.01
- if equity_krw >= EQUITY_TIER_1B:
- return 0.05
- if equity_krw >= EQUITY_TIER_100M:
- return 0.10
- return None
-
-
-def max_buy_from_cash(equity_krw: float, cash_krw: float) -> float:
- """구간별 규칙을 반영한 1회 매수 최대 금액.
-
- Args:
- equity_krw: 현재 총평가금액(원).
- cash_krw: 보유 현금(원).
-
- Returns:
- 매수에 사용 가능한 최대 원화.
- """
- cash = max(float(cash_krw), 0.0)
- pct = buy_cash_pct(equity_krw)
- if pct is None:
- return cash
- return cash * pct
-
-
-def buy_sizing_metadata() -> dict[str, Any]:
- """시뮬 결과·차트에 포함할 매수 상한 메타."""
- return {
- "buy_sizing_rule": BUY_SIZING_RULE_LABEL,
- "buy_sizing_tiers": [
- {"min_equity_krw": EQUITY_TIER_100M, "max_cash_pct": 0.10},
- {"min_equity_krw": EQUITY_TIER_1B, "max_cash_pct": 0.05},
- {"min_equity_krw": EQUITY_TIER_10B, "max_cash_pct": 0.01},
- ],
- }
diff --git a/src/deepcoin/mtf/__init__.py b/src/deepcoin/mtf/__init__.py
deleted file mode 100644
index a02d2fa..0000000
--- a/src/deepcoin/mtf/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-"""멀티 타임프레임(MTF) 인과 피처 추출."""
-
-from deepcoin.mtf.alignment import as_of_from_signal_bar, last_complete_bar_index
-from deepcoin.mtf.extractor import MtfFeatureExtractor, MtfSnapshot
-from deepcoin.mtf.filter import MtfSignalFilter, score_mtf_rules
-from deepcoin.mtf.rules import MtfRule, MtfRuleSet, derive_rules_from_report, load_mtf_rules, save_mtf_rules
-from deepcoin.mtf.store import MTF_INTERVALS, MultiTimeframeStore
-
-__all__ = [
- "MTF_INTERVALS",
- "MultiTimeframeStore",
- "MtfFeatureExtractor",
- "MtfSnapshot",
- "MtfSignalFilter",
- "MtfRule",
- "MtfRuleSet",
- "derive_rules_from_report",
- "load_mtf_rules",
- "save_mtf_rules",
- "score_mtf_rules",
- "as_of_from_signal_bar",
- "last_complete_bar_index",
-]
diff --git a/src/deepcoin/operations/__init__.py b/src/deepcoin/operations/__init__.py
deleted file mode 100644
index 72590a3..0000000
--- a/src/deepcoin/operations/__init__.py
+++ /dev/null
@@ -1,12 +0,0 @@
-"""현물 3단계 운영 — composite_v3 + MTF."""
-
-from deepcoin.operations.backtest import run_filtered_backtest, save_backtest_report
-from deepcoin.operations.runner import OperationsRunner
-from deepcoin.operations.signal_pipeline import run_signal_pipeline
-
-__all__ = [
- "OperationsRunner",
- "run_filtered_backtest",
- "run_signal_pipeline",
- "save_backtest_report",
-]