feat(ops): sim 튜닝 사이징·일 체결 10000·매수 안전버퍼 5000원
3년 sim 기반 sizing_rules를 저장소에 포함하고, live 매수 시 수수료 lock과 5000원 잔여 현금을 확보하도록 운영 기본값을 갱신한다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
10
.env.example
10
.env.example
@@ -75,16 +75,16 @@ OPS_MODE=paper
|
||||
OPS_TECHNIQUE_ID=fractal_swing
|
||||
OPS_MTF_ENABLED=false
|
||||
OPS_TREND_GATE_ENABLED=false
|
||||
OPS_DAILY_MAX_TRADES=100
|
||||
OPS_DAILY_MAX_TRADES=10000
|
||||
OPS_MIN_ORDER_KRW=5000
|
||||
# 1회 매수·매도 분할 (0.10=총평가/보유의 10%, 20만원→약 2만원/회)
|
||||
# 튜닝 기준값 — 실제 비율은 1_tune_order_sizing.py → sizing_rules.json
|
||||
OPS_BUY_CASH_PCT=0.10
|
||||
OPS_SELL_COIN_PCT=0.10
|
||||
OPS_SLIPPAGE_RATE=0.0005
|
||||
# 빗썸 시장가 매수 시 주문금액+예약수수료 lock (전액 주문 400 방지)
|
||||
# 빗썸 시장가 매수: 주문금액+예약수수료 lock (insufficient_funds 방지)
|
||||
OPS_EXCHANGE_FEE_LOCK_RATE=0.0025
|
||||
# live 시장가 매수 안전 여유: floor(가용/(1+lock)) - N원
|
||||
OPS_BUY_SAFETY_BUFFER_KRW=1000
|
||||
# live 매수 상한: floor(가용/(1+lock)) - N원 — N=최소 잔여 현금(수수료 lock 별도)
|
||||
OPS_BUY_SAFETY_BUFFER_KRW=5000
|
||||
OPS_ORDER_INTERVAL_SEC=0.35
|
||||
OPS_SYNC_CANDLES=true
|
||||
# 비우면 DOWNLOAD_INTERVALS 전체 증분 sync
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -10,6 +10,9 @@
|
||||
!/data/spot/mtf/
|
||||
/data/spot/mtf/**
|
||||
!data/spot/mtf/mtf_rules_v3.json
|
||||
!/data/spot/operations/
|
||||
/data/spot/operations/**
|
||||
!data/spot/operations/sizing_rules.json
|
||||
/docs/**
|
||||
!/docs/spot/
|
||||
/docs/spot/**
|
||||
|
||||
94
data/spot/operations/sizing_rules.json
Normal file
94
data/spot/operations/sizing_rules.json
Normal file
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"generated_at": "2026-06-13 18:04:03",
|
||||
"technique_id": "fractal_swing",
|
||||
"symbol": "BTC",
|
||||
"default_buy_cash_pct": 1.0,
|
||||
"default_sell_coin_pct": 1.0,
|
||||
"by_cluster": {
|
||||
"buy": {
|
||||
"1": 1.0,
|
||||
"2": 0.7
|
||||
},
|
||||
"sell": {
|
||||
"1": 1.0,
|
||||
"2": 1.0
|
||||
}
|
||||
},
|
||||
"tuning": {
|
||||
"objective": "total_return_pct",
|
||||
"pct_candidates": [
|
||||
0.1,
|
||||
0.15,
|
||||
0.2,
|
||||
0.25,
|
||||
0.3,
|
||||
0.4,
|
||||
0.5,
|
||||
0.6,
|
||||
0.7,
|
||||
0.8,
|
||||
1.0
|
||||
],
|
||||
"min_bucket_samples": 5,
|
||||
"cluster_counts": {
|
||||
"buy": {
|
||||
"1": 56440,
|
||||
"2": 185
|
||||
},
|
||||
"sell": {
|
||||
"1": 56441,
|
||||
"2": 184
|
||||
}
|
||||
},
|
||||
"history": [
|
||||
{
|
||||
"step": "global_buy",
|
||||
"buy_pct": 1.0,
|
||||
"return_pct": 83276.93
|
||||
},
|
||||
{
|
||||
"step": "global_sell",
|
||||
"sell_pct": 1.0,
|
||||
"return_pct": 2307591.86
|
||||
},
|
||||
{
|
||||
"step": "refine_buy",
|
||||
"buy_pct": 1.0,
|
||||
"return_pct": 2307591.86
|
||||
},
|
||||
{
|
||||
"step": "refine_sell",
|
||||
"sell_pct": 1.0,
|
||||
"return_pct": 2307591.86
|
||||
},
|
||||
{
|
||||
"step": "bucket_buy_1",
|
||||
"pct": 1.0,
|
||||
"samples": 56440,
|
||||
"return_pct": 2307591.86
|
||||
},
|
||||
{
|
||||
"step": "bucket_buy_2",
|
||||
"pct": 0.7,
|
||||
"samples": 185,
|
||||
"return_pct": 2312689.34
|
||||
},
|
||||
{
|
||||
"step": "bucket_sell_1",
|
||||
"pct": 1.0,
|
||||
"samples": 56441,
|
||||
"return_pct": 2312689.34
|
||||
},
|
||||
{
|
||||
"step": "bucket_sell_2",
|
||||
"pct": 1.0,
|
||||
"samples": 184,
|
||||
"return_pct": 2312689.34
|
||||
}
|
||||
],
|
||||
"baseline_return_pct": 3907.55,
|
||||
"final_return_pct": 2312689.34,
|
||||
"final_buys_executed": 56810,
|
||||
"final_sells_executed": 56808
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,8 @@ def main() -> int:
|
||||
print("\n=== 매수·매도 비율 튜닝 (타점 고정) ===", flush=True)
|
||||
print(
|
||||
f"기법: {settings.ops_technique_id} · sim {settings.gt_sim_lookback_days}일 · "
|
||||
f"기본 {settings.ops_buy_cash_pct:.0%}/{settings.ops_sell_coin_pct:.0%}",
|
||||
f"일 체결 상한 {settings.ops_daily_max_trades} · "
|
||||
f"튜닝 시드 {settings.ops_buy_cash_pct:.0%}/{settings.ops_sell_coin_pct:.0%}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
|
||||
@@ -276,7 +276,7 @@ def load_settings(env_path: Path | None = None) -> Settings:
|
||||
os.getenv("OPS_EXCHANGE_FEE_LOCK_RATE", "0.0025")
|
||||
),
|
||||
ops_buy_safety_buffer_krw=float(
|
||||
os.getenv("OPS_BUY_SAFETY_BUFFER_KRW", "1000")
|
||||
os.getenv("OPS_BUY_SAFETY_BUFFER_KRW", "5000")
|
||||
),
|
||||
ops_buy_cash_pct=float(os.getenv("OPS_BUY_CASH_PCT", "0.10")),
|
||||
ops_sell_coin_pct=float(os.getenv("OPS_SELL_COIN_PCT", "0.10")),
|
||||
|
||||
@@ -44,11 +44,11 @@ def spendable_cash_for_exchange_buy(
|
||||
fee_lock_rate: float = 0.0025,
|
||||
safety_buffer_krw: float = 1000.0,
|
||||
) -> float:
|
||||
"""거래소 시장가 매수 시 주문 가능 원화 (수수료 lock + floor + 안전 여유).
|
||||
"""거래소 시장가 매수 시 주문 가능 원화 (예약수수료 lock + 최소 잔여 현금).
|
||||
|
||||
빗썸은 주문금액 + 예약수수료를 KRW에서 동시에 lock하므로
|
||||
가용 원화 전액을 price로 넣으면 insufficient_funds(400)가 발생한다.
|
||||
``floor(가용/(1+lock)) - safety_buffer_krw`` 로 주문 상한을 계산한다.
|
||||
빗썸은 주문금액 + 예약수수료(fee_lock_rate)를 KRW에서 동시에 lock한다.
|
||||
``floor(가용/(1+lock)) - safety_buffer_krw`` 로 주문 상한을 계산하며,
|
||||
safety_buffer_krw(기본 5000)는 매수 후에도 남길 최소 현금(원)이다.
|
||||
"""
|
||||
cash = max(float(cash_krw), 0.0)
|
||||
lock = max(float(fee_lock_rate), 0.0)
|
||||
|
||||
Reference in New Issue
Block a user