11
This commit is contained in:
parent
7a7f7261a9
commit
476cfef193
@ -80,7 +80,8 @@ ALPHAX_PAPER_ORDER_GATE_ENABLED=1
|
||||
ALPHAX_PAPER_MAX_CUMULATIVE_LEVERAGE=5
|
||||
ALPHAX_PAPER_ENTRY_GATE_ENABLED=1
|
||||
ALPHAX_PAPER_ENTRY_MIN_REC_SCORE=50
|
||||
ALPHAX_PAPER_ENTRY_MIN_RR=1.8
|
||||
ALPHAX_PAPER_MIN_RR=1.5
|
||||
ALPHAX_PAPER_ENTRY_MIN_RR=1.5
|
||||
ALPHAX_PAPER_MAX_STOP_LOSS_LEVERAGE_RISK_PCT=20
|
||||
ALPHAX_PAPER_MAX_ACCOUNT_DRAWDOWN_PAUSE_PCT=3
|
||||
ALPHAX_PAPER_PAUSE_AFTER_WEAK_ENTRIES=3
|
||||
@ -97,9 +98,9 @@ ALPHAX_PAPER_GLOBAL_RISK_MAX_OPEN_POSITIONS=0
|
||||
ALPHAX_PAPER_GLOBAL_RISK_MAX_SAME_SECTOR_POSITIONS=3
|
||||
ALPHAX_PAPER_GLOBAL_RISK_MAX_SAME_DIRECTION_POSITIONS=6
|
||||
ALPHAX_PAPER_ORDER_MIN_REC_SCORE=50
|
||||
ALPHAX_PAPER_ORDER_MIN_RR=1.8
|
||||
ALPHAX_PAPER_ORDER_MIN_RR=1.5
|
||||
ALPHAX_PAPER_ORDER_REQUIRE_RISK_REWARD_OK=1
|
||||
ALPHAX_PAPER_ORDER_MIN_DISTANCE_TO_ENTRY_PCT=1.5
|
||||
ALPHAX_PAPER_ORDER_MIN_DISTANCE_TO_ENTRY_PCT=0
|
||||
ALPHAX_PAPER_ORDER_MAX_DISTANCE_TO_ENTRY_PCT=8
|
||||
ALPHAX_PAPER_ORDER_REQUIRE_CURRENT_TRIGGER=0
|
||||
ALPHAX_PAPER_ORDER_CANCEL_FAR_FROM_ENTRY_PCT=12
|
||||
|
||||
@ -150,6 +150,10 @@ def _onchain_env_overrides(default_chains=("ethereum", "bsc")):
|
||||
|
||||
|
||||
def default_paper_trading_config():
|
||||
# One shared default keeps buy-now entries and wait-pullback orders from
|
||||
# drifting into two unrelated RR standards. The explicit entry/order envs
|
||||
# remain supported for advanced overrides.
|
||||
paper_min_rr = _env_float("ALPHAX_PAPER_MIN_RR", 1.5)
|
||||
return {
|
||||
"enabled": _env_bool("ALPHAX_PAPER_TRADING_ENABLED", True),
|
||||
"account_equity_usdt": _env_float("ALPHAX_PAPER_ACCOUNT_EQUITY_USDT", 20000),
|
||||
@ -174,7 +178,8 @@ def default_paper_trading_config():
|
||||
"order_gate_enabled": _env_bool("ALPHAX_PAPER_ORDER_GATE_ENABLED", True),
|
||||
"entry_gate_enabled": _env_bool("ALPHAX_PAPER_ENTRY_GATE_ENABLED", True),
|
||||
"entry_min_rec_score": _env_float("ALPHAX_PAPER_ENTRY_MIN_REC_SCORE", 50.0),
|
||||
"entry_min_rr": _env_float("ALPHAX_PAPER_ENTRY_MIN_RR", 1.8),
|
||||
"min_rr": paper_min_rr,
|
||||
"entry_min_rr": _env_float("ALPHAX_PAPER_ENTRY_MIN_RR", paper_min_rr),
|
||||
"max_stop_loss_leverage_risk_pct": _env_float("ALPHAX_PAPER_MAX_STOP_LOSS_LEVERAGE_RISK_PCT", 20.0),
|
||||
"max_account_drawdown_pause_pct": _env_float("ALPHAX_PAPER_MAX_ACCOUNT_DRAWDOWN_PAUSE_PCT", 3.0),
|
||||
"pause_after_weak_entries": _env_int("ALPHAX_PAPER_PAUSE_AFTER_WEAK_ENTRIES", 3),
|
||||
@ -191,7 +196,7 @@ def default_paper_trading_config():
|
||||
"global_risk_max_same_sector_positions": _env_int("ALPHAX_PAPER_GLOBAL_RISK_MAX_SAME_SECTOR_POSITIONS", 3),
|
||||
"global_risk_max_same_direction_positions": _env_int("ALPHAX_PAPER_GLOBAL_RISK_MAX_SAME_DIRECTION_POSITIONS", 6),
|
||||
"order_min_rec_score": _env_float("ALPHAX_PAPER_ORDER_MIN_REC_SCORE", 50.0),
|
||||
"order_min_rr": _env_float("ALPHAX_PAPER_ORDER_MIN_RR", 1.8),
|
||||
"order_min_rr": _env_float("ALPHAX_PAPER_ORDER_MIN_RR", paper_min_rr),
|
||||
"order_require_risk_reward_ok": _env_bool("ALPHAX_PAPER_ORDER_REQUIRE_RISK_REWARD_OK", True),
|
||||
"order_min_distance_to_entry_pct": _env_float("ALPHAX_PAPER_ORDER_MIN_DISTANCE_TO_ENTRY_PCT", 1.5),
|
||||
"order_max_distance_to_entry_pct": _env_float("ALPHAX_PAPER_ORDER_MAX_DISTANCE_TO_ENTRY_PCT", 8.0),
|
||||
@ -206,6 +211,58 @@ def default_paper_trading_config():
|
||||
}
|
||||
|
||||
|
||||
def _paper_trading_env_overrides():
|
||||
"""Honor explicit paper-trading env vars even when DB runtime config exists."""
|
||||
overrides = {}
|
||||
if _env_present("ALPHAX_PAPER_MIN_RR"):
|
||||
shared_min_rr = _env_float("ALPHAX_PAPER_MIN_RR", 1.5)
|
||||
overrides.update({
|
||||
"min_rr": shared_min_rr,
|
||||
"entry_min_rr": shared_min_rr,
|
||||
"order_min_rr": shared_min_rr,
|
||||
})
|
||||
checks = {
|
||||
"ALPHAX_PAPER_TRADING_ENABLED": ("enabled", lambda: _env_bool("ALPHAX_PAPER_TRADING_ENABLED", True)),
|
||||
"ALPHAX_PAPER_ACCOUNT_EQUITY_USDT": ("account_equity_usdt", lambda: _env_float("ALPHAX_PAPER_ACCOUNT_EQUITY_USDT", 20000)),
|
||||
"ALPHAX_PAPER_TRADE_NOTIONAL_USDT": ("trade_notional_usdt", lambda: _env_float("ALPHAX_PAPER_TRADE_NOTIONAL_USDT", 5000)),
|
||||
"ALPHAX_PAPER_TRADE_LEVERAGE": ("trade_leverage", lambda: _env_float("ALPHAX_PAPER_TRADE_LEVERAGE", 5)),
|
||||
"ALPHAX_PAPER_MAX_CUMULATIVE_LEVERAGE": ("max_cumulative_leverage", lambda: _env_float("ALPHAX_PAPER_MAX_CUMULATIVE_LEVERAGE", 5.0)),
|
||||
"ALPHAX_PAPER_TRADE_FEE_RATE": ("fee_rate", lambda: _env_float("ALPHAX_PAPER_TRADE_FEE_RATE", 0.001)),
|
||||
"ALPHAX_PAPER_TRADE_SLIPPAGE_PCT": ("slippage_pct", lambda: _env_float("ALPHAX_PAPER_TRADE_SLIPPAGE_PCT", 0.05)),
|
||||
"ALPHAX_PAPER_ORDER_GATE_ENABLED": ("order_gate_enabled", lambda: _env_bool("ALPHAX_PAPER_ORDER_GATE_ENABLED", True)),
|
||||
"ALPHAX_PAPER_ENTRY_GATE_ENABLED": ("entry_gate_enabled", lambda: _env_bool("ALPHAX_PAPER_ENTRY_GATE_ENABLED", True)),
|
||||
"ALPHAX_PAPER_ENTRY_MIN_REC_SCORE": ("entry_min_rec_score", lambda: _env_float("ALPHAX_PAPER_ENTRY_MIN_REC_SCORE", 50.0)),
|
||||
"ALPHAX_PAPER_ENTRY_MIN_RR": ("entry_min_rr", lambda: _env_float("ALPHAX_PAPER_ENTRY_MIN_RR", overrides.get("min_rr", 1.5))),
|
||||
"ALPHAX_PAPER_MAX_STOP_LOSS_LEVERAGE_RISK_PCT": ("max_stop_loss_leverage_risk_pct", lambda: _env_float("ALPHAX_PAPER_MAX_STOP_LOSS_LEVERAGE_RISK_PCT", 20.0)),
|
||||
"ALPHAX_PAPER_MAX_ACCOUNT_DRAWDOWN_PAUSE_PCT": ("max_account_drawdown_pause_pct", lambda: _env_float("ALPHAX_PAPER_MAX_ACCOUNT_DRAWDOWN_PAUSE_PCT", 3.0)),
|
||||
"ALPHAX_PAPER_PAUSE_AFTER_WEAK_ENTRIES": ("pause_after_weak_entries", lambda: _env_int("ALPHAX_PAPER_PAUSE_AFTER_WEAK_ENTRIES", 3)),
|
||||
"ALPHAX_PAPER_WEAK_ENTRY_WINDOW_HOURS": ("weak_entry_window_hours", lambda: _env_float("ALPHAX_PAPER_WEAK_ENTRY_WINDOW_HOURS", 6.0)),
|
||||
"ALPHAX_PAPER_WEAK_ENTRY_MIN_MAX_PNL_PCT": ("weak_entry_min_max_pnl_pct", lambda: _env_float("ALPHAX_PAPER_WEAK_ENTRY_MIN_MAX_PNL_PCT", 1.0)),
|
||||
"ALPHAX_PAPER_GLOBAL_RISK_GATE_ENABLED": ("global_risk_gate_enabled", lambda: _env_bool("ALPHAX_PAPER_GLOBAL_RISK_GATE_ENABLED", True)),
|
||||
"ALPHAX_PAPER_GLOBAL_RISK_BLOCK_CRITICAL": ("global_risk_block_critical", lambda: _env_bool("ALPHAX_PAPER_GLOBAL_RISK_BLOCK_CRITICAL", False)),
|
||||
"ALPHAX_PAPER_GLOBAL_RISK_CRITICAL_MIN_REC_SCORE": ("global_risk_critical_min_rec_score", lambda: _env_float("ALPHAX_PAPER_GLOBAL_RISK_CRITICAL_MIN_REC_SCORE", 80.0)),
|
||||
"ALPHAX_PAPER_GLOBAL_RISK_MIN_POSITION_MULTIPLIER": ("global_risk_min_position_multiplier", lambda: _env_float("ALPHAX_PAPER_GLOBAL_RISK_MIN_POSITION_MULTIPLIER", 0.2)),
|
||||
"ALPHAX_PAPER_GLOBAL_RISK_HIGH_MIN_REC_SCORE": ("global_risk_high_min_rec_score", lambda: _env_float("ALPHAX_PAPER_GLOBAL_RISK_HIGH_MIN_REC_SCORE", 70.0)),
|
||||
"ALPHAX_PAPER_GLOBAL_RISK_HIGH_DRAWDOWN_PCT": ("global_risk_high_drawdown_pct", lambda: _env_float("ALPHAX_PAPER_GLOBAL_RISK_HIGH_DRAWDOWN_PCT", 3.0)),
|
||||
"ALPHAX_PAPER_GLOBAL_RISK_CRITICAL_DRAWDOWN_PCT": ("global_risk_critical_drawdown_pct", lambda: _env_float("ALPHAX_PAPER_GLOBAL_RISK_CRITICAL_DRAWDOWN_PCT", 6.0)),
|
||||
"ALPHAX_PAPER_GLOBAL_RISK_MAX_OPEN_POSITIONS": ("global_risk_max_open_positions", lambda: _env_int("ALPHAX_PAPER_GLOBAL_RISK_MAX_OPEN_POSITIONS", 0)),
|
||||
"ALPHAX_PAPER_GLOBAL_RISK_MAX_SAME_SECTOR_POSITIONS": ("global_risk_max_same_sector_positions", lambda: _env_int("ALPHAX_PAPER_GLOBAL_RISK_MAX_SAME_SECTOR_POSITIONS", 3)),
|
||||
"ALPHAX_PAPER_GLOBAL_RISK_MAX_SAME_DIRECTION_POSITIONS": ("global_risk_max_same_direction_positions", lambda: _env_int("ALPHAX_PAPER_GLOBAL_RISK_MAX_SAME_DIRECTION_POSITIONS", 6)),
|
||||
"ALPHAX_PAPER_ORDER_MIN_REC_SCORE": ("order_min_rec_score", lambda: _env_float("ALPHAX_PAPER_ORDER_MIN_REC_SCORE", 50.0)),
|
||||
"ALPHAX_PAPER_ORDER_MIN_RR": ("order_min_rr", lambda: _env_float("ALPHAX_PAPER_ORDER_MIN_RR", overrides.get("min_rr", 1.5))),
|
||||
"ALPHAX_PAPER_ORDER_REQUIRE_RISK_REWARD_OK": ("order_require_risk_reward_ok", lambda: _env_bool("ALPHAX_PAPER_ORDER_REQUIRE_RISK_REWARD_OK", True)),
|
||||
"ALPHAX_PAPER_ORDER_MIN_DISTANCE_TO_ENTRY_PCT": ("order_min_distance_to_entry_pct", lambda: _env_float("ALPHAX_PAPER_ORDER_MIN_DISTANCE_TO_ENTRY_PCT", 0.0)),
|
||||
"ALPHAX_PAPER_ORDER_MAX_DISTANCE_TO_ENTRY_PCT": ("order_max_distance_to_entry_pct", lambda: _env_float("ALPHAX_PAPER_ORDER_MAX_DISTANCE_TO_ENTRY_PCT", 8.0)),
|
||||
"ALPHAX_PAPER_ORDER_REQUIRE_CURRENT_TRIGGER": ("order_require_current_trigger", lambda: _env_bool("ALPHAX_PAPER_ORDER_REQUIRE_CURRENT_TRIGGER", False)),
|
||||
"ALPHAX_PAPER_ORDER_CANCEL_FAR_FROM_ENTRY_PCT": ("order_cancel_far_from_entry_pct", lambda: _env_float("ALPHAX_PAPER_ORDER_CANCEL_FAR_FROM_ENTRY_PCT", 12.0)),
|
||||
"ALPHAX_PAPER_ORDER_EXPIRE_HOURS": ("order_expire_hours", lambda: _env_float("ALPHAX_PAPER_ORDER_EXPIRE_HOURS", 24.0)),
|
||||
}
|
||||
for env_name, (key, loader) in checks.items():
|
||||
if _env_present(env_name):
|
||||
overrides[key] = loader()
|
||||
return overrides
|
||||
|
||||
|
||||
def default_live_trading_config():
|
||||
return {
|
||||
"enabled": _env_bool("ALPHAX_LIVE_TRADING_ENABLED", False),
|
||||
@ -457,7 +514,8 @@ def paper_trading_config():
|
||||
if cfg is None:
|
||||
_seed_one("paper_trading", default_paper_trading_config(), "Paper trading account and execution model")
|
||||
cfg = get_paper_trading_config(default=None)
|
||||
return deep_merge(default_paper_trading_config(), cfg or {})
|
||||
merged = deep_merge(default_paper_trading_config(), cfg or {})
|
||||
return deep_merge(merged, _paper_trading_env_overrides())
|
||||
|
||||
|
||||
def live_trading_config():
|
||||
|
||||
@ -664,14 +664,20 @@ def _open_trade(conn, rec: dict, current_price: float, event_time: str, config:
|
||||
notional = default_notional_usdt(cfg)
|
||||
side = str(plan.get("side") or rec.get("side") or "long").strip().lower() or "long"
|
||||
leverage = default_leverage(cfg)
|
||||
stop_loss = _safe_float(rec.get("stop_loss") or plan.get("stop_loss"))
|
||||
tp1 = _safe_float(rec.get("tp1") or plan.get("tp1") or plan.get("take_profit_1"))
|
||||
stop_loss = _safe_float(plan.get("stop_loss") or rec.get("stop_loss"))
|
||||
tp1 = _safe_float(plan.get("tp1") or plan.get("take_profit_1") or rec.get("tp1"))
|
||||
rec_score = _safe_float(rec.get("rec_score") or rec.get("score"))
|
||||
if rec_score <= 0 and rec_id > 0:
|
||||
row = conn.execute("SELECT rec_score FROM recommendation WHERE id=%s", (rec_id,)).fetchone()
|
||||
rec_score = _safe_float(row["rec_score"] if row else 0)
|
||||
if bool(cfg.get("entry_gate_enabled", True)):
|
||||
rr = _safe_float(plan.get("rr1") or plan.get("rr1_live")) or _trade_rr(side, entry_price, stop_loss, tp1)
|
||||
calc_rr = _trade_rr(side, entry_price, stop_loss, tp1)
|
||||
rr_candidates = [
|
||||
_safe_float(plan.get("rr1")),
|
||||
_safe_float(plan.get("rr1_live")),
|
||||
calc_rr,
|
||||
]
|
||||
rr = max([x for x in rr_candidates if x > 0], default=0.0)
|
||||
min_rr = max(0.0, _safe_float(cfg.get("entry_min_rr"), 0))
|
||||
min_score = max(0.0, _safe_float(cfg.get("entry_min_rec_score"), 0))
|
||||
sl_risk = _stop_loss_leverage_risk_pct(side, entry_price, stop_loss, leverage)
|
||||
@ -904,8 +910,8 @@ def _paper_order_gate(rec: dict, current_price: float, config: dict | None = Non
|
||||
plan = _entry_plan(rec)
|
||||
side = str(plan.get("side") or rec.get("side") or "long").strip().lower() or "long"
|
||||
target = _paper_order_target_price(rec)
|
||||
stop_loss = _safe_float(rec.get("stop_loss") or plan.get("stop_loss"))
|
||||
tp1 = _safe_float(rec.get("tp1") or plan.get("tp1") or plan.get("take_profit_1"))
|
||||
stop_loss = _safe_float(plan.get("stop_loss") or rec.get("stop_loss"))
|
||||
tp1 = _safe_float(plan.get("tp1") or plan.get("take_profit_1") or rec.get("tp1"))
|
||||
rr = _safe_float(plan.get("rr1") or plan.get("rr1_live"))
|
||||
calc_rr = _paper_order_rr(side, target, stop_loss, tp1)
|
||||
# Wait-pullback orders must be judged at the intended limit price, not at
|
||||
@ -1043,9 +1049,9 @@ def _order_payload_from_rec(rec: dict, current_price: float, event_time: str, co
|
||||
"target_price": _paper_order_target_price(rec),
|
||||
"current_price_at_create": current_price,
|
||||
"notional_usdt": default_notional_usdt(cfg),
|
||||
"stop_loss": _safe_float(rec.get("stop_loss") or plan.get("stop_loss")),
|
||||
"tp1": _safe_float(rec.get("tp1") or plan.get("tp1") or plan.get("take_profit_1")),
|
||||
"tp2": _safe_float(rec.get("tp2") or plan.get("tp2") or plan.get("take_profit_2")),
|
||||
"stop_loss": _safe_float(plan.get("stop_loss") or rec.get("stop_loss")),
|
||||
"tp1": _safe_float(plan.get("tp1") or plan.get("take_profit_1") or rec.get("tp1")),
|
||||
"tp2": _safe_float(plan.get("tp2") or plan.get("take_profit_2") or rec.get("tp2")),
|
||||
"strategy_version": str(rec.get("strategy_version") or ""),
|
||||
"strategy_code": lineage["strategy_code"],
|
||||
"strategy_name": lineage["strategy_name"],
|
||||
|
||||
@ -340,7 +340,7 @@ def derive_execution_fields(item):
|
||||
if item.get("latest_cache_updated_at"):
|
||||
item["current_price_updated_at"] = item.get("latest_cache_updated_at")
|
||||
entry_window = entry_window_policy(
|
||||
item.get("entry_price") or entry_plan.get("entry_price") or 0,
|
||||
entry_plan.get("entry_price") or item.get("entry_price") or 0,
|
||||
current_price_for_window,
|
||||
item.get("rec_time") or "",
|
||||
) if action_status == "可即刻买入" else {}
|
||||
@ -399,9 +399,9 @@ def derive_execution_fields(item):
|
||||
if entry_window and entry_window.get("status") != "active":
|
||||
item["entry_window_alert"] = entry_window
|
||||
item["risk_suggestion"] = risk_suggestion(
|
||||
item.get("entry_price") or entry_plan.get("entry_price") or 0,
|
||||
item.get("stop_loss") or entry_plan.get("stop_loss") or 0,
|
||||
item.get("tp1") or entry_plan.get("tp1") or entry_plan.get("take_profit_1") or 0,
|
||||
entry_plan.get("entry_price") or item.get("entry_price") or 0,
|
||||
entry_plan.get("stop_loss") or item.get("stop_loss") or 0,
|
||||
entry_plan.get("tp1") or entry_plan.get("take_profit_1") or item.get("tp1") or 0,
|
||||
)
|
||||
item["market_context"] = market_context
|
||||
item["derivatives_context"] = derivatives_context
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -426,6 +426,42 @@ def test_buy_now_rejects_large_leveraged_stop_loss(monkeypatch):
|
||||
assert list_paper_trades()["total"] == 0
|
||||
|
||||
|
||||
def test_buy_now_entry_gate_uses_latest_entry_plan_rr(monkeypatch):
|
||||
monkeypatch.setenv("ALPHAX_PAPER_TRADING_ENABLED", "1")
|
||||
monkeypatch.setenv("ALPHAX_PAPER_ENTRY_GATE_ENABLED", "1")
|
||||
monkeypatch.setenv("ALPHAX_PAPER_ENTRY_MIN_REC_SCORE", "50")
|
||||
monkeypatch.setenv("ALPHAX_PAPER_ENTRY_MIN_RR", "1.5")
|
||||
monkeypatch.setenv("ALPHAX_PAPER_TRADE_SLIPPAGE_PCT", "0")
|
||||
altcoin_db.init_db()
|
||||
rec_id = altcoin_db.create_recommendation(
|
||||
symbol="PLANRR/USDT",
|
||||
rec_state="爆发",
|
||||
rec_score=67,
|
||||
entry_price=0.0913,
|
||||
stop_loss=0.085064,
|
||||
tp1=0.0993,
|
||||
signals=["当前15min即刻入场信号"],
|
||||
entry_plan={
|
||||
"entry_action": "可即刻买入",
|
||||
"entry_price": 0.0899,
|
||||
"current_price": 0.0899,
|
||||
"stop_loss": 0.085064,
|
||||
"tp1": 0.098243,
|
||||
"risk_reward_ok": True,
|
||||
"rr1": 1.73,
|
||||
"entry_trigger_confirmed": True,
|
||||
},
|
||||
)
|
||||
rec = next(r for r in altcoin_db.get_active_recommendations_deduped(actionable_only=False) if r["id"] == rec_id)
|
||||
|
||||
result = sync_recommendation(rec, 0.0899, event_time="2026-05-16T10:00:00")
|
||||
|
||||
assert result["opened"] is True
|
||||
trade = list_paper_trades()["items"][0]
|
||||
assert trade["stop_loss"] == pytest.approx(0.085064)
|
||||
assert trade["tp1"] == pytest.approx(0.098243)
|
||||
|
||||
|
||||
def test_buy_now_pauses_when_portfolio_drawdown_exceeded(monkeypatch, buy_now_rec):
|
||||
monkeypatch.setenv("ALPHAX_PAPER_TRADING_ENABLED", "1")
|
||||
monkeypatch.setenv("ALPHAX_PAPER_ACCOUNT_EQUITY_USDT", "1000")
|
||||
|
||||
@ -190,6 +190,32 @@ class RecommendationSignalTrustTests(unittest.TestCase):
|
||||
self.assertAlmostEqual(target['entry_window']['current_price'], 10.12, places=4)
|
||||
self.assertAlmostEqual(target['entry_window']['deviation_pct'], 1.2, places=2)
|
||||
|
||||
def test_entry_window_uses_latest_entry_plan_price_before_stale_top_level_price(self):
|
||||
self._insert_rec(
|
||||
rec_time=datetime.now().strftime('%Y-%m-%dT%H:%M:%S'),
|
||||
entry_price=10.0,
|
||||
current_price=9.75,
|
||||
stop_loss=9.4,
|
||||
tp1=10.8,
|
||||
entry_plan_json=json.dumps({
|
||||
'entry_price': 9.8,
|
||||
'entry_action': '可即刻买入',
|
||||
'risk_reward_ok': True,
|
||||
'entry_trigger_confirmed': True,
|
||||
'rr1': 2.0,
|
||||
'stop_loss': 9.4,
|
||||
'tp1': 10.8,
|
||||
}, ensure_ascii=False),
|
||||
)
|
||||
|
||||
rows = altcoin_db.get_active_recommendations_deduped(actionable_only=False, version='v-test')
|
||||
target = rows[0]
|
||||
|
||||
self.assertEqual(target['entry_window']['status'], 'active')
|
||||
self.assertEqual(target['execution_status'], 'buy_now')
|
||||
self.assertAlmostEqual(target['entry_window']['entry_price'], 9.8, places=4)
|
||||
self.assertAlmostEqual(target['entry_window']['deviation_pct'], -0.51, places=2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user