88b7fb7a0a
Forked from crypto_monitor OKX instance (copy-only). Keeps standalone options, perp-options and options-options hedge; drops standalone perp entrypoints; forces coin margin; adds sim wallets; default port 5010. Co-authored-by: Cursor <cursoragent@cursor.com>
165 lines
5.6 KiB
Python
165 lines
5.6 KiB
Python
"""关键位程序自动下单开关(三所共用,与 POSITION_SIZING_MODE 联动)."""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from typing import Any, Optional, Sequence, Tuple
|
|
|
|
from lib.key_monitor.fib_key_monitor_lib import is_fib_key_monitor_type
|
|
from lib.key_monitor.false_breakout_key_monitor_lib import is_false_breakout_key_monitor_type
|
|
from lib.key_monitor.key_monitor_full_margin_lib import monitor_type_disallowed_in_full_margin
|
|
from lib.key_monitor.key_monitor_lib import KEY_MONITOR_AUTO_TYPES, KEY_MONITOR_RS_TYPES
|
|
from lib.key_monitor.trigger_entry_key_monitor_lib import is_trigger_entry_key_monitor_type
|
|
from lib.trade.position_sizing_lib import is_full_margin_mode
|
|
|
|
KEY_ENTRY_REASON_OPTIONS: Tuple[str, ...] = (
|
|
"关键位箱体突破",
|
|
"关键位收敛突破",
|
|
"关键位斐波0.618",
|
|
"关键位斐波0.786",
|
|
"关键位假突破",
|
|
"关键位回调触价开仓",
|
|
"关键位突破触价开仓",
|
|
)
|
|
|
|
KEY_ENTRY_REASON_TRIGGER_OPTIONS: frozenset[str] = frozenset(
|
|
{
|
|
"关键位回调触价开仓",
|
|
"关键位突破触价开仓",
|
|
}
|
|
)
|
|
|
|
KEY_STATS_SEGMENT_KEYS: frozenset[str] = frozenset(
|
|
{
|
|
"key_box",
|
|
"key_conv",
|
|
"key_fib618",
|
|
"key_fib786",
|
|
"key_false_breakout",
|
|
"key_trigger",
|
|
}
|
|
)
|
|
|
|
KEY_STATS_TRIGGER_ONLY: frozenset[str] = frozenset({"key_trigger"})
|
|
|
|
TREND_MANUAL_ENTRY_REASON_COUNT = 5
|
|
|
|
|
|
def _env_bool(raw: Optional[str], default: bool = False) -> bool:
|
|
if raw is None:
|
|
return default
|
|
return (raw or "").strip().lower() in ("1", "true", "yes", "on")
|
|
|
|
|
|
def load_key_auto_order_enabled(env: Optional[dict] = None) -> bool:
|
|
e = env if env is not None else os.environ
|
|
return _env_bool(e.get("KEY_AUTO_ORDER_ENABLED"), default=False)
|
|
|
|
|
|
def is_key_level_entry_reason(reason: str) -> bool:
|
|
return (reason or "").strip() in KEY_ENTRY_REASON_OPTIONS
|
|
|
|
|
|
def visible_key_entry_reasons(sizing_mode: str, key_auto_enabled: bool) -> Tuple[str, ...]:
|
|
if not key_auto_enabled:
|
|
return ()
|
|
if is_full_margin_mode(sizing_mode):
|
|
return tuple(x for x in KEY_ENTRY_REASON_OPTIONS if x in KEY_ENTRY_REASON_TRIGGER_OPTIONS)
|
|
return KEY_ENTRY_REASON_OPTIONS
|
|
|
|
|
|
def effective_entry_reason_options(
|
|
all_options: Sequence[str],
|
|
sizing_mode: str,
|
|
key_auto_enabled: bool,
|
|
*,
|
|
trend_manual_count: int = TREND_MANUAL_ENTRY_REASON_COUNT,
|
|
) -> Tuple[str, ...]:
|
|
"""复盘/表单下拉:按开关与计仓模式裁剪关键位开仓类型."""
|
|
opts = list(all_options)
|
|
if len(opts) <= trend_manual_count:
|
|
return tuple(opts)
|
|
key_visible = set(visible_key_entry_reasons(sizing_mode, key_auto_enabled))
|
|
out: list[str] = []
|
|
for i, item in enumerate(opts):
|
|
if i < trend_manual_count:
|
|
out.append(item)
|
|
elif is_key_level_entry_reason(item):
|
|
if item in key_visible:
|
|
out.append(item)
|
|
else:
|
|
out.append(item)
|
|
return tuple(out)
|
|
|
|
|
|
def effective_stats_segment_defs(
|
|
segment_defs: Sequence[Tuple[str, str, Any]],
|
|
sizing_mode: str,
|
|
key_auto_enabled: bool,
|
|
) -> Tuple[Tuple[str, str, Any], ...]:
|
|
if not key_auto_enabled:
|
|
hidden = KEY_STATS_SEGMENT_KEYS
|
|
elif is_full_margin_mode(sizing_mode):
|
|
hidden = KEY_STATS_SEGMENT_KEYS - KEY_STATS_TRIGGER_ONLY
|
|
else:
|
|
hidden = frozenset()
|
|
return tuple(x for x in segment_defs if x[0] not in hidden)
|
|
|
|
|
|
def is_key_auto_monitor_type(monitor_type: str) -> bool:
|
|
mt = (monitor_type or "").strip()
|
|
if mt in KEY_MONITOR_AUTO_TYPES:
|
|
return True
|
|
if is_fib_key_monitor_type(mt):
|
|
return True
|
|
if is_false_breakout_key_monitor_type(mt):
|
|
return True
|
|
if is_trigger_entry_key_monitor_type(mt):
|
|
return True
|
|
return False
|
|
|
|
|
|
def is_rs_key_monitor_type(monitor_type: str) -> bool:
|
|
return (monitor_type or "").strip() in KEY_MONITOR_RS_TYPES
|
|
|
|
|
|
def check_monitor_type_add_allowed(
|
|
monitor_type: str,
|
|
sizing_mode: str,
|
|
key_auto_enabled: bool,
|
|
) -> Tuple[bool, str]:
|
|
mt = (monitor_type or "").strip()
|
|
if is_rs_key_monitor_type(mt):
|
|
return True, ""
|
|
if not key_auto_enabled:
|
|
return False, (
|
|
"已关闭关键位程序自动单(KEY_AUTO_ORDER_ENABLED=false);"
|
|
"仅可添加「关键支撑阻力」(微信提醒,不下单)."
|
|
)
|
|
if is_full_margin_mode(sizing_mode):
|
|
if monitor_type_disallowed_in_full_margin(mt):
|
|
return False, (
|
|
"全仓杠杆模式下不可添加箱体/收敛突破,斐波或假突破监控;"
|
|
"可使用「回调/突破触价开仓」或关键支撑阻力(仅提醒)."
|
|
)
|
|
if not is_key_auto_monitor_type(mt) and not is_rs_key_monitor_type(mt):
|
|
return False, "监控类型无效"
|
|
return True, ""
|
|
|
|
|
|
def key_auto_order_env_comment_lines() -> Tuple[str, ...]:
|
|
return (
|
|
"# 关键位程序自动下单(与 POSITION_SIZING_MODE 联动,修改后须重启 PM2)",
|
|
"# 默认 false = 关闭所有关键位程序自动单(箱体/收敛/斐波/假突破/触价)",
|
|
"#",
|
|
"# POSITION_SIZING_MODE=risk(以损定仓)",
|
|
"# false → 不执行任何关键位自动单;支撑/阻力提醒,人工下单,顺势加仓不受影响",
|
|
"# true → 允许关键位全套自动(含触价)",
|
|
"#",
|
|
"# POSITION_SIZING_MODE=full_margin(全仓杠杆,须无仓切换)",
|
|
"# false → 不执行触价自动单",
|
|
"# true → 仅回调/突破触价可程序自动开仓;箱体/斐波等仍禁止",
|
|
"#",
|
|
"# 顺势加仓,趋势回调不受本开关控制;全仓模式下策略自动仍禁止.",
|
|
"KEY_AUTO_ORDER_ENABLED=false",
|
|
)
|