首次上传

This commit is contained in:
dekun
2026-05-16 22:25:48 +08:00
commit 2b8f902548
88 changed files with 16386 additions and 0 deletions
+167
View File
@@ -0,0 +1,167 @@
from __future__ import annotations
from pathlib import Path
from typing import Literal
import yaml
from pydantic import BaseModel, Field, ValidationError
class AppConfig(BaseModel):
host: str = "0.0.0.0"
port: int = 8088
poll_interval_seconds: int = 120
log_file: str = "./runtime/system.log"
database_url: str = "sqlite+aiosqlite:///./runtime/alerts.db"
session_secret: str = "please-change-me"
class AuthConfig(BaseModel):
"""
enabled: 为 false 时跳过登录(仅建议纯局域网、无外网暴露时使用)。
"""
enabled: bool = True
username: str
password: str
class WeComConfig(BaseModel):
webhook: str
mentioned_mobile_list: list[str] = Field(default_factory=list)
class GateConfig(BaseModel):
"""Gate.io 公共 REST v4USDT 永续 settle=usdt)。"""
api_base: str = "https://api.gateio.ws/api/v4"
settle: str = "usdt"
quote_currency: str = "USDT"
class ProxyConfig(BaseModel):
"""
出站 HTTP 客户端代理(httpx),用于访问 Gate 等外网。
企业微信与本机/局域网 Ollama(Gemma)默认直连,不使用此配置。
可写 socks5h://…;程序在交给 httpx 时会自动改为 socks5://(避免 Unknown scheme)。
"""
enabled: bool = False
url: str = "socks5h://127.0.0.1:1080"
class OrderExecutorConfig(BaseModel):
"""
与 gate_order_executor 联动:企微突破推送 **成功之后**,向执行器 POST /v1/signal。
请求不走 proxy.url(直连 base_url),便于同机 127.0.0.1。
webhook_secret 须与执行器 config.yaml 的 security.webhook_secret 一致。
"""
enabled: bool = False
base_url: str = "http://127.0.0.1:8090"
webhook_secret: str = ""
timeout_seconds: float = Field(15.0, ge=3.0, le=120.0)
class WatchSymbol(BaseModel):
"""Gate USDT 永续 base 资产符号,如 BTC、ORDI、1000PEPE(与合约名 BTC_USDT 的左侧一致)。"""
symbol: str
class MonitorConfig(BaseModel):
"""
监控侧过滤。
universe:
- all_swaps: 监控 Gate 全部 USDT 本位线性永续中,24h 成交额达标的合约(不依赖 watch_symbols)。
- watchlist: 仅监控 watch_symbols 中列出且满足成交额阈值的标的。
min_24h_quote_volume_usdt: 近 24h 成交额下限(USDT)。优先使用 Gate ticker 的 volume_24h_quote。
all_swaps 模式下若设为 0 或负数,将拒绝整轮扫描(避免无阈值拉全市场)。
watchlist 模式下 0 表示关闭成交额过滤。
btc_daily_gate_enabled: 可选;true 时仍计算 BTC 日线 regime 供面板/日志参考,不再拦截山寨扫描。
btc_sideways_lookback_days / btc_sideways_max_range_pct: 与上述辅助门控配套的横盘区分参数。
"""
universe: Literal["all_swaps", "watchlist"] = "all_swaps"
min_24h_quote_volume_usdt: float = 10_000_000
# 可选:BTC 日线 regime 仅展示/记录;推送门控用「近8h×15m BTC 环境(横盘则多空均可;否则涨→LONG、跌→SHORT)+ 本币4h同向」
btc_daily_gate_enabled: bool = True
btc_sideways_lookback_days: int = 14
btc_sideways_max_range_pct: float = 10.0
# 同一币种在 N 小时内对同一条「链路」只落库一条告警、只推送一次(0 表示关闭去重)
# 链路含:GATE-USDT 5m WATCH / GATE-USDT 5m TRIGGER(分级)与 FUNNEL-GEMMA(漏斗)
symbol_signal_dedupe_hours: float = 4.0
# 企业微信主推送(突破预警):仅对本轮监控池内 24h 成交额排名前 N 的合约推送;0 表示不限制
wecom_push_max_volume_rank: int = 30
class GemmaConfig(BaseModel):
"""
本地 Ollama 跑 Gemma(或其它模型)做漏斗二次分拣。
需在机器上自行启动 ollama 并拉取模型;开启后仅对本轮 5m 扫描命中的 WATCH/TRIGGER 按成交额取前 N 再请求。
"""
enabled: bool = False
ollama_base_url: str = "http://127.0.0.1:11434"
model: str = "gemma2:2b"
timeout_seconds: float = 180.0
temperature: float = 0.15
json_mode: bool = True
send_chart_image: bool = True
max_funnel_per_cycle: int = 12
vision_top_n: int = 4
gemma_push_priority_min: float = 7.0
composite_push_min: float = 72.0
class DailyReportConfig(BaseModel):
"""每日晨报:北京时间定时生成昨天复盘,并可推送企业微信。"""
enabled: bool = True
run_time_cn: str = "08:30"
push_wecom: bool = True
run_on_startup: bool = False
class Settings(BaseModel):
app: AppConfig
auth: AuthConfig
wecom: WeComConfig
gate: GateConfig
proxy: ProxyConfig = Field(default_factory=ProxyConfig)
order_executor: OrderExecutorConfig = Field(default_factory=OrderExecutorConfig)
monitor: MonitorConfig = Field(default_factory=MonitorConfig)
gemma: GemmaConfig = Field(default_factory=GemmaConfig)
daily_report: DailyReportConfig = Field(default_factory=DailyReportConfig)
watch_symbols: list[WatchSymbol] = Field(default_factory=list)
def load_settings(config_path: str = "config.yaml") -> Settings:
path = Path(config_path).expanduser().resolve()
if not path.exists():
raise FileNotFoundError(
f"配置文件不存在: {path}. 请先复制 config.example.yaml 为 config.yaml 并填写密钥。"
)
raw = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
try:
return Settings.model_validate(raw)
except ValidationError as exc:
raise ValueError(f"配置文件校验失败: {exc}") from exc
# 兼容原 OKX 风格 bar 字符串(映射见 app.gate._to_gate_interval
GATE_BAR_CHOICES: tuple[str, ...] = (
"1m",
"3m",
"5m",
"15m",
"30m",
"1H",
"2H",
"4H",
"6H",
"12H",
"1D",
"1W",
"1M",
)