refactor: 将共用代码迁入 lib/ 模块化目录

统一 strategy、key_monitor、trade、hub 等共用库到 lib/ 子包,并补充 lib-structure 文档,便于四所与中控维护。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-02 16:23:09 +08:00
parent 4742a0bb9d
commit 5797d49d8a
190 changed files with 27946 additions and 27499 deletions
+5 -2
View File
@@ -53,8 +53,11 @@ bash deploy/setup_env.sh --install-system-deps
| `crypto_monitor_gate_bot/` | Gate 机器人 / 趋势户 | [部署文档.md](./crypto_monitor_gate_bot/部署文档.md) | | `crypto_monitor_gate_bot/` | Gate 机器人 / 趋势户 | [部署文档.md](./crypto_monitor_gate_bot/部署文档.md) |
| `crypto_monitor_okx/` | OKX 永续 | [部署文档.md](./crypto_monitor_okx/部署文档.md) | | `crypto_monitor_okx/` | OKX 永续 | [部署文档.md](./crypto_monitor_okx/部署文档.md) |
| `manual_trading_hub/` | 中控 + 子代理 | [部署文档.md](./manual_trading_hub/部署文档.md) | | `manual_trading_hub/` | 中控 + 子代理 | [部署文档.md](./manual_trading_hub/部署文档.md) |
| 根目录 `strategy_*.py` | 策略共用库 | [策略交易说明.md](./策略交易说明.md) | | `lib/` | **共用模块**(策略、关键位、交易、中控库、AI、静态与模板) | **[docs/lib-structure.md](./docs/lib-structure.md)** |
| 根目录 `key_*_lib.py` | 关键位 / 止盈止损共用库 | [关键位止盈止损与移动保本更新说明.md](./关键位止盈止损与移动保本更新说明.md) | | `brand/` | 各所共用图标与 manifest | — |
| `docs/``deploy/``scripts/``tests/` | 文档、环境、脚本、单元测试 | — |
共用代码 import 示例:`from lib.strategy.strategy_db import init_strategy_tables`(各所启动时仍将仓库根加入 `PYTHONPATH`)。详见 **[docs/lib-structure.md](./docs/lib-structure.md)**。
--- ---
+61 -60
View File
@@ -34,14 +34,15 @@ import sys
if _REPO_ROOT not in sys.path: if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT) sys.path.insert(0, _REPO_ROOT)
from ai_client import ai_generate, ai_review, ai_short_advice from lib.paths import common_static_dir
from ai_review_lib import ( from lib.ai.ai_client import ai_generate, ai_review, ai_short_advice
from lib.ai.ai_review_lib import (
build_journal_ai_chart_path, build_journal_ai_chart_path,
collect_images_for_ai_review, collect_images_for_ai_review,
journal_row_lines_for_ai, journal_row_lines_for_ai,
) )
from form_submit_lib import check_duplicate_submit, submit_scope_add_key, submit_scope_add_order from lib.common.form_submit_lib import check_duplicate_submit, submit_scope_add_key, submit_scope_add_order
from fib_key_monitor_lib import ( from lib.key_monitor.fib_key_monitor_lib import (
FIB_KEY_MONITOR_TYPES, FIB_KEY_MONITOR_TYPES,
backfill_missing_key_signal_types, backfill_missing_key_signal_types,
calc_fib_plan, calc_fib_plan,
@@ -52,7 +53,7 @@ from fib_key_monitor_lib import (
key_signal_type_for_trade_record, key_signal_type_for_trade_record,
stored_key_signal_type, stored_key_signal_type,
) )
from false_breakout_key_monitor_lib import ( from lib.key_monitor.false_breakout_key_monitor_lib import (
FALSE_BREAKOUT_MONITOR_TYPE, FALSE_BREAKOUT_MONITOR_TYPE,
FALSE_BREAKOUT_VALIDITY_HOURS, FALSE_BREAKOUT_VALIDITY_HOURS,
calc_false_breakout_plan, calc_false_breakout_plan,
@@ -65,7 +66,7 @@ from false_breakout_key_monitor_lib import (
normalize_false_breakout_symbol, normalize_false_breakout_symbol,
storage_bounds_from_key_price, storage_bounds_from_key_price,
) )
from strategy_trade_labels import ( from lib.strategy.strategy_trade_labels import (
STRATEGY_ENTRY_REASON_OPTIONS, STRATEGY_ENTRY_REASON_OPTIONS,
apply_order_monitor_source_labels, apply_order_monitor_source_labels,
entry_reason_for_monitor_type, entry_reason_for_monitor_type,
@@ -74,7 +75,7 @@ from strategy_trade_labels import (
trade_record_monitor_type as resolve_trade_record_monitor_type, trade_record_monitor_type as resolve_trade_record_monitor_type,
trend_plan_id_from_monitor_row, trend_plan_id_from_monitor_row,
) )
from journal_chart_lib import ( from lib.instance.journal_chart_lib import (
JOURNAL_CHART_DEFAULT_LIMIT, JOURNAL_CHART_DEFAULT_LIMIT,
JOURNAL_CHART_DEFAULT_TF1, JOURNAL_CHART_DEFAULT_TF1,
JOURNAL_CHART_DEFAULT_TF2, JOURNAL_CHART_DEFAULT_TF2,
@@ -90,7 +91,7 @@ from journal_chart_lib import (
trade_review_fetch_window, trade_review_fetch_window,
trim_rows_for_trade_review, trim_rows_for_trade_review,
) )
from key_sl_tp_lib import ( from lib.key_monitor.key_sl_tp_lib import (
breakeven_enabled_from_row, breakeven_enabled_from_row,
normalize_sl_tp_mode, normalize_sl_tp_mode,
parse_breakeven_enabled_form, parse_breakeven_enabled_form,
@@ -99,7 +100,7 @@ from key_sl_tp_lib import (
sl_tp_mode_label, sl_tp_mode_label,
sl_tp_plan_summary_text, sl_tp_plan_summary_text,
) )
from time_close_lib import ( from lib.trade.time_close_lib import (
TIME_CLOSE_RESULT, TIME_CLOSE_RESULT,
apply_time_close_to_payload, apply_time_close_to_payload,
ensure_time_close_schema, ensure_time_close_schema,
@@ -110,13 +111,13 @@ from time_close_lib import (
time_close_label, time_close_label,
time_close_settings_from_row, time_close_settings_from_row,
) )
from manual_sltp_lib import ( from lib.trade.manual_sltp_lib import (
normalize_open_sltp_mode, normalize_open_sltp_mode,
resolve_entrust_sltp_prices, resolve_entrust_sltp_prices,
resolve_open_sltp_prices, resolve_open_sltp_prices,
) )
from key_monitor_schema_lib import ensure_key_monitor_schema from lib.key_monitor.key_monitor_schema_lib import ensure_key_monitor_schema
from trigger_entry_key_monitor_lib import ( from lib.key_monitor.trigger_entry_key_monitor_lib import (
BREAKOUT_TRIGGER_ENTRY_MONITOR_TYPE, BREAKOUT_TRIGGER_ENTRY_MONITOR_TYPE,
CALLBACK_TRIGGER_ENTRY_MONITOR_TYPE, CALLBACK_TRIGGER_ENTRY_MONITOR_TYPE,
TRIGGER_ENTRY_CLOSE_EXCHANGE_FAILED, TRIGGER_ENTRY_CLOSE_EXCHANGE_FAILED,
@@ -139,7 +140,7 @@ from trigger_entry_key_monitor_lib import (
validate_trigger_entry_geometry, validate_trigger_entry_geometry,
validate_trigger_entry_rr, validate_trigger_entry_rr,
) )
from position_sizing_lib import ( from lib.trade.position_sizing_lib import (
OPEN_SOURCE_KEY_AUTO, OPEN_SOURCE_KEY_AUTO,
OPEN_SOURCE_KEY_TRIGGER, OPEN_SOURCE_KEY_TRIGGER,
OPEN_SOURCE_MANUAL, OPEN_SOURCE_MANUAL,
@@ -155,12 +156,12 @@ from position_sizing_lib import (
mode_label_zh, mode_label_zh,
risk_percent_for_storage, risk_percent_for_storage,
) )
from key_monitor_full_margin_lib import ( from lib.key_monitor.key_monitor_full_margin_lib import (
monitor_type_disallowed_in_full_margin, monitor_type_disallowed_in_full_margin,
purge_disallowed_key_monitors, purge_disallowed_key_monitors,
) )
from auto_transfer_daily_lib import run_auto_transfer_once_per_day from lib.common.auto_transfer_daily_lib import run_auto_transfer_once_per_day
from key_monitor_lib import ( from lib.key_monitor.key_monitor_lib import (
KEY_DIRECTION_WATCH, KEY_DIRECTION_WATCH,
KEY_MONITOR_ALERT_ONLY_TYPES, KEY_MONITOR_ALERT_ONLY_TYPES,
KEY_MONITOR_AUTO_TYPES, KEY_MONITOR_AUTO_TYPES,
@@ -180,15 +181,15 @@ from key_monitor_lib import (
rs_break_from_direction, rs_break_from_direction,
run_rs_level_alert_tick, run_rs_level_alert_tick,
) )
from order_monitor_display_lib import ( from lib.trade.order_monitor_display_lib import (
apply_order_price_display_fields, apply_order_price_display_fields,
enrich_order_display_fields, enrich_order_display_fields,
order_monitor_tpsl_needs_sync, order_monitor_tpsl_needs_sync,
) )
from wechat_notify_lib import build_wechat_rs_level_message, send_wechat_webhook from lib.common.wechat_notify_lib import build_wechat_rs_level_message, send_wechat_webhook
from hub_auth import request_allowed as hub_request_allowed from lib.hub.hub_auth import request_allowed as hub_request_allowed
from hub_volume_rank_lib import resolve_daily_volume_rank from lib.hub.hub_volume_rank_lib import resolve_daily_volume_rank
from history_window_lib import ( from lib.common.history_window_lib import (
PRESET_CUSTOM, PRESET_CUSTOM,
PRESET_UTC_LAST24H, PRESET_UTC_LAST24H,
PRESET_UTC_LAST7D, PRESET_UTC_LAST7D,
@@ -201,8 +202,8 @@ from history_window_lib import (
utc_window_to_bj_sql_strings, utc_window_to_bj_sql_strings,
utc_window_to_utc_sql_strings, utc_window_to_utc_sql_strings,
) )
from trade_result_lib import count_winning_trades, normalize_result_with_pnl from lib.trade.trade_result_lib import count_winning_trades, normalize_result_with_pnl
from trade_exchange_stats_lib import ( from lib.trade.trade_exchange_stats_lib import (
attach_exchange_stats_to_trade, attach_exchange_stats_to_trade,
filter_position_lifecycle_fills, filter_position_lifecycle_fills,
sum_binance_commission_income, sum_binance_commission_income,
@@ -353,7 +354,7 @@ ORDER_CHART_ENABLED = os.getenv("ORDER_CHART_ENABLED", "true").lower() == "true"
ORDER_CHART_TFS = [x.strip() for x in (os.getenv("ORDER_CHART_TFS", "4h,1h,15m,5m") or "").split(",") if x.strip()] ORDER_CHART_TFS = [x.strip() for x in (os.getenv("ORDER_CHART_TFS", "4h,1h,15m,5m") or "").split(",") if x.strip()]
ORDER_CHART_LIMIT = int(os.getenv("ORDER_CHART_LIMIT", "100")) ORDER_CHART_LIMIT = int(os.getenv("ORDER_CHART_LIMIT", "100"))
ORDER_CHART_DIR = resolve_path(os.getenv("ORDER_CHART_DIR", "static/images/order_charts")) ORDER_CHART_DIR = resolve_path(os.getenv("ORDER_CHART_DIR", "static/images/order_charts"))
from daily_open_limit_lib import ( from lib.trade.daily_open_limit_lib import (
build_daily_open_alert_prompt, build_daily_open_alert_prompt,
can_trade_new_open, can_trade_new_open,
check_daily_open_hard_limit, check_daily_open_hard_limit,
@@ -1520,10 +1521,10 @@ def init_db():
close_reason TEXT, closed_at TEXT)""" close_reason TEXT, closed_at TEXT)"""
) )
from strategy_db import init_strategy_tables from lib.strategy.strategy_db import init_strategy_tables
init_strategy_tables(conn) init_strategy_tables(conn)
from account_risk_lib import ensure_account_risk_schema from lib.trade.account_risk_lib import ensure_account_risk_schema
ensure_account_risk_schema(conn) ensure_account_risk_schema(conn)
backfill_missing_key_signal_types(conn, monitor_type=ORDER_MONITOR_TYPE_KEY_AUTO) backfill_missing_key_signal_types(conn, monitor_type=ORDER_MONITOR_TYPE_KEY_AUTO)
@@ -1560,7 +1561,7 @@ def get_db():
def hub_account_risk_status(conn): def hub_account_risk_status(conn):
from account_risk_lib import ( from lib.trade.account_risk_lib import (
apply_position_limit_risk, apply_position_limit_risk,
compute_account_risk_status, compute_account_risk_status,
enrich_risk_status_countdown, enrich_risk_status_countdown,
@@ -1576,7 +1577,7 @@ def hub_account_risk_status(conn):
fmt_local_ms=ms_to_app_local_str, fmt_local_ms=ms_to_app_local_str,
) )
st = enrich_risk_status_countdown(st, now=now, daily_reset_hour=TRADING_DAY_RESET_HOUR) st = enrich_risk_status_countdown(st, now=now, daily_reset_hour=TRADING_DAY_RESET_HOUR)
from strategy_trade_labels import count_position_limit_active_monitors from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
return apply_position_limit_risk( return apply_position_limit_risk(
st, st,
@@ -1593,7 +1594,7 @@ def hub_user_initiated_close(
trade_record_id=None, trade_record_id=None,
closed_at_ms=None, closed_at_ms=None,
): ):
from account_risk_lib import CLOSE_SOURCE_USER_HUB, on_user_initiated_close from lib.trade.account_risk_lib import CLOSE_SOURCE_USER_HUB, on_user_initiated_close
src = (source or "").strip() or CLOSE_SOURCE_USER_HUB src = (source or "").strip() or CLOSE_SOURCE_USER_HUB
on_user_initiated_close( on_user_initiated_close(
@@ -2120,7 +2121,7 @@ def get_effective_trade_field(row, reviewed_key, base_key, default=None):
def to_effective_trade_dict(row): def to_effective_trade_dict(row):
item = row_to_dict(row) item = row_to_dict(row)
from order_monitor_display_lib import snapshot_stop_loss from lib.trade.order_monitor_display_lib import snapshot_stop_loss
open_stop = snapshot_stop_loss(item.get("initial_stop_loss"), item.get("stop_loss")) open_stop = snapshot_stop_loss(item.get("initial_stop_loss"), item.get("stop_loss"))
item["display_open_stop_loss"] = open_stop item["display_open_stop_loss"] = open_stop
@@ -2661,7 +2662,7 @@ def insert_trade_record(
open_ts_ms = _to_ms_with_fallback(opened_at_ms, open_ts) open_ts_ms = _to_ms_with_fallback(opened_at_ms, open_ts)
close_ts_ms = _to_ms_with_fallback(closed_at_ms, close_ts) close_ts_ms = _to_ms_with_fallback(closed_at_ms, close_ts)
kst = key_signal_type_for_trade_record(key_signal_type, KEY_MONITOR_AUTO_TYPES) kst = key_signal_type_for_trade_record(key_signal_type, KEY_MONITOR_AUTO_TYPES)
from order_monitor_display_lib import snapshot_stop_loss from lib.trade.order_monitor_display_lib import snapshot_stop_loss
snap_sl = snapshot_stop_loss(initial_stop_loss, stop_loss) snap_sl = snapshot_stop_loss(initial_stop_loss, stop_loss)
er = ( er = (
@@ -3193,7 +3194,7 @@ def resolve_capital_base_for_key_open(conn, trading_day, live_capital):
def precheck_risk(conn, symbol, direction): def precheck_risk(conn, symbol, direction):
now = app_now() now = app_now()
from account_risk_lib import account_risk_blocks_trading from lib.trade.account_risk_lib import account_risk_blocks_trading
ok_risk, risk_reason = account_risk_blocks_trading( ok_risk, risk_reason = account_risk_blocks_trading(
conn, conn,
@@ -3205,7 +3206,7 @@ def precheck_risk(conn, symbol, direction):
return False, risk_reason return False, risk_reason
if not trading_day_reset_allows_new_open(now): if not trading_day_reset_allows_new_open(now):
return False, f"北京时间 {TRADING_DAY_RESET_HOUR}:00 前不允许持仓" return False, f"北京时间 {TRADING_DAY_RESET_HOUR}:00 前不允许持仓"
from account_risk_lib import position_limit_reached from lib.trade.account_risk_lib import position_limit_reached
reached, active_count, mx = position_limit_reached(conn, max_active_positions=MAX_ACTIVE_POSITIONS) reached, active_count, mx = position_limit_reached(conn, max_active_positions=MAX_ACTIVE_POSITIONS)
if reached: if reached:
@@ -3898,7 +3899,7 @@ def list_orphan_live_positions(conn):
ex = normalize_exchange_symbol(r["exchange_symbol"] or r["symbol"]) ex = normalize_exchange_symbol(r["exchange_symbol"] or r["symbol"])
active_keys.add((ex, (r["direction"] or "long").strip().lower())) active_keys.add((ex, (r["direction"] or "long").strip().lower()))
from hub_position_metrics import parse_position_entry_price from lib.hub.hub_position_metrics import parse_position_entry_price
orphans = [] orphans = []
for lp in live_rows: for lp in live_rows:
@@ -4097,7 +4098,7 @@ def parse_ccxt_position_metrics(position, order_leverage=None):
cs = float(get_contract_size(sym)) if sym else 1.0 cs = float(get_contract_size(sym)) if sym else 1.0
except Exception: except Exception:
cs = 1.0 cs = 1.0
from hub_position_metrics import enrich_ccxt_position_metrics_out from lib.hub.hub_position_metrics import enrich_ccxt_position_metrics_out
enrich_ccxt_position_metrics_out( enrich_ccxt_position_metrics_out(
p, out, contract_size=cs, funds_decimals=FUNDS_DECIMALS p, out, contract_size=cs, funds_decimals=FUNDS_DECIMALS
@@ -6807,14 +6808,14 @@ def background_task():
check_trigger_entry_key_monitors() check_trigger_entry_key_monitors()
_roll_cfg = app.extensions.get("strategy_roll_cfg") _roll_cfg = app.extensions.get("strategy_roll_cfg")
if _roll_cfg: if _roll_cfg:
from strategy_roll_monitor_lib import check_roll_monitors from lib.strategy.strategy_roll_monitor_lib import check_roll_monitors
check_roll_monitors(_roll_cfg) check_roll_monitors(_roll_cfg)
check_key_monitors() check_key_monitors()
check_order_monitors() check_order_monitors()
cfg = app.extensions.get("strategy_trend_cfg") cfg = app.extensions.get("strategy_trend_cfg")
if cfg: if cfg:
from strategy_trend_register import check_trend_pullback_plans from lib.strategy.strategy_trend_register import check_trend_pullback_plans
check_trend_pullback_plans(cfg) check_trend_pullback_plans(cfg)
except: except:
@@ -7006,7 +7007,7 @@ def render_main_page(page="trade", embed_mode=None):
conn = get_db() conn = get_db()
session_row = ensure_session(conn, trading_day) session_row = ensure_session(conn, trading_day)
local_current_capital = float(session_row["current_capital"]) local_current_capital = float(session_row["current_capital"])
from instance_embed_context_lib import ( from lib.instance.instance_embed_context_lib import (
embed_render_plan, embed_render_plan,
minimal_stats_bundle, minimal_stats_bundle,
trade_records_summary, trade_records_summary,
@@ -7070,7 +7071,7 @@ def render_main_page(page="trade", embed_mode=None):
records = [] records = []
total = miss_count = rate = occupied_miss_total = 0 total = miss_count = rate = occupied_miss_total = 0
active_count = len(order_list) active_count = len(order_list)
from strategy_trade_labels import count_position_limit_active_monitors from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
position_limit_count = count_position_limit_active_monitors(conn) position_limit_count = count_position_limit_active_monitors(conn)
opens_today = count_opens_for_trading_day(conn, trading_day) opens_today = count_opens_for_trading_day(conn, trading_day)
@@ -7101,7 +7102,7 @@ def render_main_page(page="trade", embed_mode=None):
) )
strategy_extra = {} strategy_extra = {}
if plan.strategy: if plan.strategy:
from strategy_ui import strategy_render_extras from lib.strategy.strategy_ui import strategy_render_extras
strategy_extra = strategy_render_extras( strategy_extra = strategy_render_extras(
conn, conn,
@@ -7114,7 +7115,7 @@ def render_main_page(page="trade", embed_mode=None):
if plan.orphan_live and not order_list and exchange_private_api_configured(): if plan.orphan_live and not order_list and exchange_private_api_configured():
orphan_live_positions = list_orphan_live_positions(conn) orphan_live_positions = list_orphan_live_positions(conn)
conn.close() conn.close()
from instance_embed_lib import embed_context_extras from lib.instance.instance_embed_lib import embed_context_extras
template_ctx = dict( template_ctx = dict(
page=page, page=page,
@@ -7236,7 +7237,7 @@ def api_account_snapshot():
funding_usdt = round(funding_capital, FUNDS_DECIMALS) if funding_capital is not None else None funding_usdt = round(funding_capital, FUNDS_DECIMALS) if funding_capital is not None else None
current_capital = round(trading_capital, FUNDS_DECIMALS) if trading_capital is not None else round(local_current_capital, FUNDS_DECIMALS) current_capital = round(trading_capital, FUNDS_DECIMALS) if trading_capital is not None else round(local_current_capital, FUNDS_DECIMALS)
recommended_capital = get_recommended_capital(current_capital) recommended_capital = get_recommended_capital(current_capital)
from strategy_trade_labels import count_position_limit_active_monitors from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
position_limit_count = count_position_limit_active_monitors(conn) position_limit_count = count_position_limit_active_monitors(conn)
opens_today = count_opens_for_trading_day(conn, trading_day) opens_today = count_opens_for_trading_day(conn, trading_day)
@@ -7523,7 +7524,7 @@ def api_price_snapshot():
pass pass
conn.close() conn.close()
from hub_position_metrics import build_position_marks_list from lib.hub.hub_position_metrics import build_position_marks_list
position_marks = build_position_marks_list( position_marks = build_position_marks_list(
all_swap_positions, all_swap_positions,
@@ -7782,7 +7783,7 @@ def api_order_kline():
"volume": float(bar[5]), "volume": float(bar[5]),
}) })
from focus_chart_lib import ( from lib.instance.focus_chart_lib import (
build_order_kline_order_payload, build_order_kline_order_payload,
load_swap_positions_for_order_kline, load_swap_positions_for_order_kline,
metrics_for_order_item, metrics_for_order_item,
@@ -7810,7 +7811,7 @@ def api_order_kline():
ex_metrics=ex_metrics, ex_metrics=ex_metrics,
) )
from focus_chart_lib import kline_api_price_fields from lib.instance.focus_chart_lib import kline_api_price_fields
price_fields = kline_api_price_fields( price_fields = kline_api_price_fields(
exchange, exchange,
@@ -7927,7 +7928,7 @@ def api_key_kline():
"lower_pct": lower_pct, "lower_pct": lower_pct,
} }
from focus_chart_lib import enrich_key_kline_response from lib.instance.focus_chart_lib import enrich_key_kline_response
price_display, key_info = enrich_key_kline_response( price_display, key_info = enrich_key_kline_response(
symbol=symbol, symbol=symbol,
@@ -7936,7 +7937,7 @@ def api_key_kline():
format_price_fn=format_price_for_symbol, format_price_fn=format_price_for_symbol,
) )
from focus_chart_lib import kline_api_price_fields from lib.instance.focus_chart_lib import kline_api_price_fields
price_fields = kline_api_price_fields( price_fields = kline_api_price_fields(
exchange, exchange,
@@ -8859,7 +8860,7 @@ def del_order(id):
opened_at=opened_at, opened_at=opened_at,
closed_at=closed_at, closed_at=closed_at,
) )
from account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close from lib.trade.account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close
on_user_initiated_close( on_user_initiated_close(
conn, conn,
@@ -8873,7 +8874,7 @@ def del_order(id):
try: try:
_rcfg = app.extensions.get("strategy_roll_cfg") _rcfg = app.extensions.get("strategy_roll_cfg")
if isinstance(_rcfg, dict): if isinstance(_rcfg, dict):
from strategy_register import roll_sync_after_external_close from lib.strategy.strategy_register import roll_sync_after_external_close
roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"]) roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"])
except Exception: except Exception:
@@ -8934,7 +8935,7 @@ def del_order(id):
opened_at=opened_at, opened_at=opened_at,
closed_at=closed_at, closed_at=closed_at,
) )
from account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close from lib.trade.account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close
on_user_initiated_close( on_user_initiated_close(
conn, conn,
@@ -8948,7 +8949,7 @@ def del_order(id):
try: try:
_rcfg = app.extensions.get("strategy_roll_cfg") _rcfg = app.extensions.get("strategy_roll_cfg")
if isinstance(_rcfg, dict): if isinstance(_rcfg, dict):
from strategy_register import roll_sync_after_external_close from lib.strategy.strategy_register import roll_sync_after_external_close
roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"]) roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"])
except Exception: except Exception:
@@ -9109,7 +9110,7 @@ def add_journal():
d.get("post_breakeven_stare"), d.get("new_trade_while_occupied"), d.get("note"), image_filename d.get("post_breakeven_stare"), d.get("new_trade_while_occupied"), d.get("note"), image_filename
) )
) )
from account_risk_lib import on_journal_saved from lib.trade.account_risk_lib import on_journal_saved
on_journal_saved( on_journal_saved(
conn, conn,
@@ -9197,7 +9198,7 @@ def api_reviews():
return jsonify([row_to_dict(r) for r in rows]) return jsonify([row_to_dict(r) for r in rows])
_REPO_STATIC_DIR = os.path.join(os.path.dirname(BASE_DIR), "static") _REPO_STATIC_DIR = common_static_dir(os.path.dirname(BASE_DIR))
_AI_REVIEW_RENDER_JS = os.path.join(_REPO_STATIC_DIR, "ai_review_render.js") _AI_REVIEW_RENDER_JS = os.path.join(_REPO_STATIC_DIR, "ai_review_render.js")
_FORM_SUBMIT_GUARD_JS = os.path.join(_REPO_STATIC_DIR, "form_submit_guard.js") _FORM_SUBMIT_GUARD_JS = os.path.join(_REPO_STATIC_DIR, "form_submit_guard.js")
_MANUAL_ORDER_RR_PREVIEW_JS = os.path.join(_REPO_STATIC_DIR, "manual_order_rr_preview.js") _MANUAL_ORDER_RR_PREVIEW_JS = os.path.join(_REPO_STATIC_DIR, "manual_order_rr_preview.js")
@@ -9422,7 +9423,7 @@ def api_trade_record_review_update():
tuple(base_params + [rec_id]), tuple(base_params + [rec_id]),
) )
if reviewed_result == "手动平仓" and reviewed_miss_reason: if reviewed_result == "手动平仓" and reviewed_miss_reason:
from account_risk_lib import apply_manual_close_journal_cooloff from lib.trade.account_risk_lib import apply_manual_close_journal_cooloff
apply_manual_close_journal_cooloff( apply_manual_close_journal_cooloff(
conn, conn,
@@ -9571,7 +9572,7 @@ def _hub_account_bundle():
def _hub_fetch_market(base=""): def _hub_fetch_market(base=""):
from hub_market_info_lib import fetch_usdt_swap_market_info from lib.hub.hub_market_info_lib import fetch_usdt_swap_market_info
return fetch_usdt_swap_market_info( return fetch_usdt_swap_market_info(
base_or_symbol=base, base_or_symbol=base,
@@ -9584,7 +9585,7 @@ def _hub_fetch_market(base=""):
def _hub_fetch_ohlcv(symbol, timeframe, since_ms=None, limit=500): def _hub_fetch_ohlcv(symbol, timeframe, since_ms=None, limit=500):
from hub_ohlcv_lib import fetch_ohlcv_for_hub from lib.hub.hub_ohlcv_lib import fetch_ohlcv_for_hub
return fetch_ohlcv_for_hub( return fetch_ohlcv_for_hub(
symbol=symbol, symbol=symbol,
@@ -9600,7 +9601,7 @@ def _hub_fetch_ohlcv(symbol, timeframe, since_ms=None, limit=500):
def _hub_fetch_volume_rank(top_n=20): def _hub_fetch_volume_rank(top_n=20):
from hub_volume_rank_lib import fetch_usdt_swap_volume_rank from lib.hub.hub_volume_rank_lib import fetch_usdt_swap_volume_rank
return fetch_usdt_swap_volume_rank( return fetch_usdt_swap_volume_rank(
exchange=exchange, exchange=exchange,
@@ -9617,7 +9618,7 @@ try:
_repo_root = Path(__file__).resolve().parent.parent _repo_root = Path(__file__).resolve().parent.parent
if str(_repo_root) not in sys.path: if str(_repo_root) not in sys.path:
sys.path.insert(0, str(_repo_root)) sys.path.insert(0, str(_repo_root))
from hub_bridge import install_on_app from lib.hub.hub_bridge import install_on_app
install_on_app( install_on_app(
app, app,
@@ -9660,8 +9661,8 @@ def strategy_roll_page():
return redirect("/strategy") return redirect("/strategy")
from strategy_register import install_strategy_trading from lib.strategy.strategy_register import install_strategy_trading
from strategy_trend_register import install_strategy_trend from lib.strategy.strategy_trend_register import install_strategy_trend
install_strategy_trading(app, _REPO_ROOT, app_module=sys.modules[__name__]) install_strategy_trading(app, _REPO_ROOT, app_module=sys.modules[__name__])
install_strategy_trend(app, _REPO_ROOT, app_module=sys.modules[__name__]) install_strategy_trend(app, _REPO_ROOT, app_module=sys.modules[__name__])
+65 -64
View File
@@ -34,14 +34,15 @@ import sys
if _REPO_ROOT not in sys.path: if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT) sys.path.insert(0, _REPO_ROOT)
from ai_client import ai_generate, ai_review, ai_short_advice from lib.paths import common_static_dir
from ai_review_lib import ( from lib.ai.ai_client import ai_generate, ai_review, ai_short_advice
from lib.ai.ai_review_lib import (
build_journal_ai_chart_path, build_journal_ai_chart_path,
collect_images_for_ai_review, collect_images_for_ai_review,
journal_row_lines_for_ai, journal_row_lines_for_ai,
) )
from form_submit_lib import check_duplicate_submit, submit_scope_add_key, submit_scope_add_order from lib.common.form_submit_lib import check_duplicate_submit, submit_scope_add_key, submit_scope_add_order
from fib_key_monitor_lib import ( from lib.key_monitor.fib_key_monitor_lib import (
FIB_KEY_MONITOR_TYPES, FIB_KEY_MONITOR_TYPES,
KEY_ENTRY_REASON_BY_SIGNAL, KEY_ENTRY_REASON_BY_SIGNAL,
backfill_missing_key_signal_types, backfill_missing_key_signal_types,
@@ -53,7 +54,7 @@ from fib_key_monitor_lib import (
key_signal_type_for_trade_record, key_signal_type_for_trade_record,
stored_key_signal_type, stored_key_signal_type,
) )
from false_breakout_key_monitor_lib import ( from lib.key_monitor.false_breakout_key_monitor_lib import (
FALSE_BREAKOUT_MONITOR_TYPE, FALSE_BREAKOUT_MONITOR_TYPE,
FALSE_BREAKOUT_VALIDITY_HOURS, FALSE_BREAKOUT_VALIDITY_HOURS,
calc_false_breakout_plan, calc_false_breakout_plan,
@@ -66,7 +67,7 @@ from false_breakout_key_monitor_lib import (
normalize_false_breakout_symbol, normalize_false_breakout_symbol,
storage_bounds_from_key_price, storage_bounds_from_key_price,
) )
from strategy_trade_labels import ( from lib.strategy.strategy_trade_labels import (
STRATEGY_ENTRY_REASON_OPTIONS, STRATEGY_ENTRY_REASON_OPTIONS,
apply_order_monitor_source_labels, apply_order_monitor_source_labels,
entry_reason_for_monitor_type, entry_reason_for_monitor_type,
@@ -75,7 +76,7 @@ from strategy_trade_labels import (
trade_record_monitor_type as resolve_trade_record_monitor_type, trade_record_monitor_type as resolve_trade_record_monitor_type,
trend_plan_id_from_monitor_row, trend_plan_id_from_monitor_row,
) )
from journal_chart_lib import ( from lib.instance.journal_chart_lib import (
JOURNAL_CHART_DEFAULT_LIMIT, JOURNAL_CHART_DEFAULT_LIMIT,
JOURNAL_CHART_DEFAULT_TF1, JOURNAL_CHART_DEFAULT_TF1,
JOURNAL_CHART_DEFAULT_TF2, JOURNAL_CHART_DEFAULT_TF2,
@@ -91,7 +92,7 @@ from journal_chart_lib import (
trade_review_fetch_window, trade_review_fetch_window,
trim_rows_for_trade_review, trim_rows_for_trade_review,
) )
from key_sl_tp_lib import ( from lib.key_monitor.key_sl_tp_lib import (
breakeven_enabled_from_row, breakeven_enabled_from_row,
normalize_sl_tp_mode, normalize_sl_tp_mode,
parse_breakeven_enabled_form, parse_breakeven_enabled_form,
@@ -100,7 +101,7 @@ from key_sl_tp_lib import (
sl_tp_mode_label, sl_tp_mode_label,
sl_tp_plan_summary_text, sl_tp_plan_summary_text,
) )
from time_close_lib import ( from lib.trade.time_close_lib import (
TIME_CLOSE_RESULT, TIME_CLOSE_RESULT,
apply_time_close_to_payload, apply_time_close_to_payload,
ensure_time_close_schema, ensure_time_close_schema,
@@ -111,13 +112,13 @@ from time_close_lib import (
time_close_label, time_close_label,
time_close_settings_from_row, time_close_settings_from_row,
) )
from manual_sltp_lib import ( from lib.trade.manual_sltp_lib import (
normalize_open_sltp_mode, normalize_open_sltp_mode,
resolve_entrust_sltp_prices, resolve_entrust_sltp_prices,
resolve_open_sltp_prices, resolve_open_sltp_prices,
) )
from key_monitor_schema_lib import ensure_key_monitor_schema from lib.key_monitor.key_monitor_schema_lib import ensure_key_monitor_schema
from trigger_entry_key_monitor_lib import ( from lib.key_monitor.trigger_entry_key_monitor_lib import (
BREAKOUT_TRIGGER_ENTRY_MONITOR_TYPE, BREAKOUT_TRIGGER_ENTRY_MONITOR_TYPE,
CALLBACK_TRIGGER_ENTRY_MONITOR_TYPE, CALLBACK_TRIGGER_ENTRY_MONITOR_TYPE,
TRIGGER_ENTRY_CLOSE_EXCHANGE_FAILED, TRIGGER_ENTRY_CLOSE_EXCHANGE_FAILED,
@@ -140,7 +141,7 @@ from trigger_entry_key_monitor_lib import (
validate_trigger_entry_geometry, validate_trigger_entry_geometry,
validate_trigger_entry_rr, validate_trigger_entry_rr,
) )
from position_sizing_lib import ( from lib.trade.position_sizing_lib import (
OPEN_SOURCE_KEY_AUTO, OPEN_SOURCE_KEY_AUTO,
OPEN_SOURCE_KEY_TRIGGER, OPEN_SOURCE_KEY_TRIGGER,
OPEN_SOURCE_MANUAL, OPEN_SOURCE_MANUAL,
@@ -154,12 +155,12 @@ from position_sizing_lib import (
mode_label_zh, mode_label_zh,
risk_percent_for_storage, risk_percent_for_storage,
) )
from key_monitor_full_margin_lib import ( from lib.key_monitor.key_monitor_full_margin_lib import (
monitor_type_disallowed_in_full_margin, monitor_type_disallowed_in_full_margin,
purge_disallowed_key_monitors, purge_disallowed_key_monitors,
) )
from auto_transfer_daily_lib import run_auto_transfer_once_per_day from lib.common.auto_transfer_daily_lib import run_auto_transfer_once_per_day
from key_monitor_lib import ( from lib.key_monitor.key_monitor_lib import (
KEY_DIRECTION_WATCH, KEY_DIRECTION_WATCH,
KEY_MONITOR_ALERT_ONLY_TYPES, KEY_MONITOR_ALERT_ONLY_TYPES,
KEY_MONITOR_AUTO_TYPES, KEY_MONITOR_AUTO_TYPES,
@@ -179,16 +180,16 @@ from key_monitor_lib import (
rs_break_from_direction, rs_break_from_direction,
run_rs_level_alert_tick, run_rs_level_alert_tick,
) )
from order_monitor_display_lib import ( from lib.trade.order_monitor_display_lib import (
apply_order_price_display_fields, apply_order_price_display_fields,
enrich_order_display_fields, enrich_order_display_fields,
order_monitor_tpsl_needs_sync, order_monitor_tpsl_needs_sync,
) )
from wechat_notify_lib import build_wechat_rs_level_message, send_wechat_webhook from lib.common.wechat_notify_lib import build_wechat_rs_level_message, send_wechat_webhook
from hub_auth import request_allowed as hub_request_allowed from lib.hub.hub_auth import request_allowed as hub_request_allowed
from instance_nav_lib import request_is_hub_soft_nav from lib.instance.instance_nav_lib import request_is_hub_soft_nav
from hub_volume_rank_lib import resolve_daily_volume_rank from lib.hub.hub_volume_rank_lib import resolve_daily_volume_rank
from history_window_lib import ( from lib.common.history_window_lib import (
PRESET_CUSTOM, PRESET_CUSTOM,
PRESET_UTC_LAST24H, PRESET_UTC_LAST24H,
PRESET_UTC_LAST7D, PRESET_UTC_LAST7D,
@@ -201,8 +202,8 @@ from history_window_lib import (
utc_window_to_bj_sql_strings, utc_window_to_bj_sql_strings,
utc_window_to_utc_sql_strings, utc_window_to_utc_sql_strings,
) )
from trade_result_lib import count_winning_trades, normalize_result_with_pnl from lib.trade.trade_result_lib import count_winning_trades, normalize_result_with_pnl
from trade_exchange_stats_lib import attach_exchange_stats_to_trade, filter_position_lifecycle_fills from lib.trade.trade_exchange_stats_lib import attach_exchange_stats_to_trade, filter_position_lifecycle_fills
def load_env_file(path): def load_env_file(path):
@@ -343,7 +344,7 @@ ORDER_CHART_ENABLED = os.getenv("ORDER_CHART_ENABLED", "true").lower() == "true"
ORDER_CHART_TFS = [x.strip() for x in (os.getenv("ORDER_CHART_TFS", "4h,1h,15m,5m") or "").split(",") if x.strip()] ORDER_CHART_TFS = [x.strip() for x in (os.getenv("ORDER_CHART_TFS", "4h,1h,15m,5m") or "").split(",") if x.strip()]
ORDER_CHART_LIMIT = int(os.getenv("ORDER_CHART_LIMIT", "100")) ORDER_CHART_LIMIT = int(os.getenv("ORDER_CHART_LIMIT", "100"))
ORDER_CHART_DIR = resolve_path(os.getenv("ORDER_CHART_DIR", "static/images/order_charts")) ORDER_CHART_DIR = resolve_path(os.getenv("ORDER_CHART_DIR", "static/images/order_charts"))
from daily_open_limit_lib import ( from lib.trade.daily_open_limit_lib import (
build_daily_open_alert_prompt, build_daily_open_alert_prompt,
can_trade_new_open, can_trade_new_open,
check_daily_open_hard_limit, check_daily_open_hard_limit,
@@ -1506,10 +1507,10 @@ def init_db():
close_reason TEXT, closed_at TEXT)""" close_reason TEXT, closed_at TEXT)"""
) )
from strategy_db import init_strategy_tables from lib.strategy.strategy_db import init_strategy_tables
init_strategy_tables(conn) init_strategy_tables(conn)
from account_risk_lib import ensure_account_risk_schema from lib.trade.account_risk_lib import ensure_account_risk_schema
ensure_account_risk_schema(conn) ensure_account_risk_schema(conn)
backfill_missing_key_signal_types(conn, monitor_type=ORDER_MONITOR_TYPE_KEY_AUTO) backfill_missing_key_signal_types(conn, monitor_type=ORDER_MONITOR_TYPE_KEY_AUTO)
@@ -1546,7 +1547,7 @@ def get_db():
def hub_account_risk_status(conn): def hub_account_risk_status(conn):
from account_risk_lib import ( from lib.trade.account_risk_lib import (
apply_position_limit_risk, apply_position_limit_risk,
compute_account_risk_status, compute_account_risk_status,
enrich_risk_status_countdown, enrich_risk_status_countdown,
@@ -1562,7 +1563,7 @@ def hub_account_risk_status(conn):
fmt_local_ms=ms_to_app_local_str, fmt_local_ms=ms_to_app_local_str,
) )
st = enrich_risk_status_countdown(st, now=now, daily_reset_hour=TRADING_DAY_RESET_HOUR) st = enrich_risk_status_countdown(st, now=now, daily_reset_hour=TRADING_DAY_RESET_HOUR)
from strategy_trade_labels import count_position_limit_active_monitors from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
return apply_position_limit_risk( return apply_position_limit_risk(
st, st,
@@ -1579,7 +1580,7 @@ def hub_user_initiated_close(
trade_record_id=None, trade_record_id=None,
closed_at_ms=None, closed_at_ms=None,
): ):
from account_risk_lib import CLOSE_SOURCE_USER_HUB, on_user_initiated_close from lib.trade.account_risk_lib import CLOSE_SOURCE_USER_HUB, on_user_initiated_close
src = (source or "").strip() or CLOSE_SOURCE_USER_HUB src = (source or "").strip() or CLOSE_SOURCE_USER_HUB
on_user_initiated_close( on_user_initiated_close(
@@ -2072,7 +2073,7 @@ def get_effective_trade_field(row, reviewed_key, base_key, default=None):
def to_effective_trade_dict(row): def to_effective_trade_dict(row):
item = row_to_dict(row) item = row_to_dict(row)
from order_monitor_display_lib import snapshot_stop_loss from lib.trade.order_monitor_display_lib import snapshot_stop_loss
open_stop = snapshot_stop_loss(item.get("initial_stop_loss"), item.get("stop_loss")) open_stop = snapshot_stop_loss(item.get("initial_stop_loss"), item.get("stop_loss"))
item["display_open_stop_loss"] = open_stop item["display_open_stop_loss"] = open_stop
@@ -2370,7 +2371,7 @@ def insert_trade_record(
open_ts_ms = _to_ms_with_fallback(opened_at_ms, open_ts) open_ts_ms = _to_ms_with_fallback(opened_at_ms, open_ts)
close_ts_ms = _to_ms_with_fallback(closed_at_ms, close_ts) close_ts_ms = _to_ms_with_fallback(closed_at_ms, close_ts)
kst = key_signal_type_for_trade_record(key_signal_type, KEY_MONITOR_AUTO_TYPES) kst = key_signal_type_for_trade_record(key_signal_type, KEY_MONITOR_AUTO_TYPES)
from order_monitor_display_lib import snapshot_stop_loss from lib.trade.order_monitor_display_lib import snapshot_stop_loss
snap_sl = snapshot_stop_loss(initial_stop_loss, stop_loss) snap_sl = snapshot_stop_loss(initial_stop_loss, stop_loss)
er = ( er = (
@@ -2761,7 +2762,7 @@ def get_exchange_capitals(force=False):
def execute_transfer_usdt(amount, from_account, to_account): def execute_transfer_usdt(amount, from_account, to_account):
from gate_transfer_lib import execute_transfer_usdt as _gate_execute_transfer_usdt from lib.exchange.gate_transfer_lib import execute_transfer_usdt as _gate_execute_transfer_usdt
return _gate_execute_transfer_usdt( return _gate_execute_transfer_usdt(
exchange, exchange,
@@ -2794,7 +2795,7 @@ def get_account_usdt_total(account_type):
def _auto_transfer_active_count(conn): def _auto_transfer_active_count(conn):
from gate_transfer_lib import count_auto_transfer_blockers from lib.exchange.gate_transfer_lib import count_auto_transfer_blockers
return count_auto_transfer_blockers(conn, count_order_monitors=get_active_position_count) return count_auto_transfer_blockers(conn, count_order_monitors=get_active_position_count)
@@ -2878,7 +2879,7 @@ def resolve_capital_base_for_key_open(conn, trading_day, live_capital):
def precheck_risk(conn, symbol, direction): def precheck_risk(conn, symbol, direction):
now = app_now() now = app_now()
from account_risk_lib import account_risk_blocks_trading from lib.trade.account_risk_lib import account_risk_blocks_trading
ok_risk, risk_reason = account_risk_blocks_trading( ok_risk, risk_reason = account_risk_blocks_trading(
conn, conn,
@@ -2890,7 +2891,7 @@ def precheck_risk(conn, symbol, direction):
return False, risk_reason return False, risk_reason
if not trading_day_reset_allows_new_open(now): if not trading_day_reset_allows_new_open(now):
return False, f"北京时间 {TRADING_DAY_RESET_HOUR}:00 前不允许持仓" return False, f"北京时间 {TRADING_DAY_RESET_HOUR}:00 前不允许持仓"
from account_risk_lib import position_limit_reached from lib.trade.account_risk_lib import position_limit_reached
reached, active_count, mx = position_limit_reached(conn, max_active_positions=MAX_ACTIVE_POSITIONS) reached, active_count, mx = position_limit_reached(conn, max_active_positions=MAX_ACTIVE_POSITIONS)
if reached: if reached:
@@ -3670,7 +3671,7 @@ def parse_ccxt_position_metrics(position, order_leverage=None):
cs = float(get_contract_size(sym)) if sym else 1.0 cs = float(get_contract_size(sym)) if sym else 1.0
except Exception: except Exception:
cs = 1.0 cs = 1.0
from hub_position_metrics import enrich_ccxt_position_metrics_out from lib.hub.hub_position_metrics import enrich_ccxt_position_metrics_out
enrich_ccxt_position_metrics_out(p, out, contract_size=cs, funds_decimals=2) enrich_ccxt_position_metrics_out(p, out, contract_size=cs, funds_decimals=2)
return out or None return out or None
@@ -3854,7 +3855,7 @@ def fetch_latest_closing_fill(exchange_symbol, direction, opened_at_str, opened_
except Exception: except Exception:
pass pass
try: try:
from gate_position_history_lib import pick_gate_position_close from lib.exchange.gate_position_history_lib import pick_gate_position_close
pos = pick_gate_position_close( pos = pick_gate_position_close(
fetch_gate_positions_close_history(), fetch_gate_positions_close_history(),
@@ -4114,7 +4115,7 @@ def reconcile_hub_external_close(conn, symbol, direction):
"""中控市价全平后:立即同步匹配 order_monitor,并读 Gate 平仓历史。""" """中控市价全平后:立即同步匹配 order_monitor,并读 Gate 平仓历史。"""
if not exchange_private_api_configured(): if not exchange_private_api_configured():
return {"ok": False, "msg": "未配置 GATE_API_KEY / GATE_API_SECRET", "synced": 0} return {"ok": False, "msg": "未配置 GATE_API_KEY / GATE_API_SECRET", "synced": 0}
from gate_position_history_lib import unified_symbol_for_match from lib.exchange.gate_position_history_lib import unified_symbol_for_match
sym_u = unified_symbol_for_match(symbol) sym_u = unified_symbol_for_match(symbol)
dir_l = (direction or "").strip().lower() dir_l = (direction or "").strip().lower()
@@ -6513,14 +6514,14 @@ def background_task():
check_trigger_entry_key_monitors() check_trigger_entry_key_monitors()
_roll_cfg = app.extensions.get("strategy_roll_cfg") _roll_cfg = app.extensions.get("strategy_roll_cfg")
if _roll_cfg: if _roll_cfg:
from strategy_roll_monitor_lib import check_roll_monitors from lib.strategy.strategy_roll_monitor_lib import check_roll_monitors
check_roll_monitors(_roll_cfg) check_roll_monitors(_roll_cfg)
check_key_monitors() check_key_monitors()
check_order_monitors() check_order_monitors()
cfg = app.extensions.get("strategy_trend_cfg") cfg = app.extensions.get("strategy_trend_cfg")
if cfg: if cfg:
from strategy_trend_register import check_trend_pullback_plans from lib.strategy.strategy_trend_register import check_trend_pullback_plans
check_trend_pullback_plans(cfg) check_trend_pullback_plans(cfg)
except: except:
@@ -6848,7 +6849,7 @@ def render_main_page(page="trade", embed_mode=None):
conn = get_db() conn = get_db()
session_row = ensure_session(conn, trading_day) session_row = ensure_session(conn, trading_day)
local_current_capital = float(session_row["current_capital"]) local_current_capital = float(session_row["current_capital"])
from instance_embed_context_lib import ( from lib.instance.instance_embed_context_lib import (
embed_render_plan, embed_render_plan,
minimal_stats_bundle, minimal_stats_bundle,
trade_records_summary, trade_records_summary,
@@ -6921,7 +6922,7 @@ def render_main_page(page="trade", embed_mode=None):
records = [] records = []
total = miss_count = rate = occupied_miss_total = 0 total = miss_count = rate = occupied_miss_total = 0
active_count = len(order_list) active_count = len(order_list)
from strategy_trade_labels import count_position_limit_active_monitors from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
position_limit_count = count_position_limit_active_monitors(conn) position_limit_count = count_position_limit_active_monitors(conn)
opens_today = count_opens_for_trading_day(conn, trading_day) opens_today = count_opens_for_trading_day(conn, trading_day)
@@ -6952,7 +6953,7 @@ def render_main_page(page="trade", embed_mode=None):
) )
strategy_extra = {} strategy_extra = {}
if plan.strategy: if plan.strategy:
from strategy_ui import strategy_render_extras from lib.strategy.strategy_ui import strategy_render_extras
strategy_extra = strategy_render_extras( strategy_extra = strategy_render_extras(
conn, conn,
@@ -6962,7 +6963,7 @@ def render_main_page(page="trade", embed_mode=None):
trend_cfg=app.extensions.get("strategy_trend_cfg"), trend_cfg=app.extensions.get("strategy_trend_cfg"),
) )
conn.close() conn.close()
from instance_embed_lib import embed_context_extras from lib.instance.instance_embed_lib import embed_context_extras
template_ctx = dict( template_ctx = dict(
page=page, page=page,
@@ -7104,7 +7105,7 @@ def api_account_snapshot():
funding_usdt = round(funding_capital, 2) if funding_capital is not None else None funding_usdt = round(funding_capital, 2) if funding_capital is not None else None
current_capital = round(trading_capital, 2) if trading_capital is not None else round(local_current_capital, 2) current_capital = round(trading_capital, 2) if trading_capital is not None else round(local_current_capital, 2)
recommended_capital = round(float(get_recommended_capital(current_capital)), 2) recommended_capital = round(float(get_recommended_capital(current_capital)), 2)
from strategy_trade_labels import count_position_limit_active_monitors from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
position_limit_count = count_position_limit_active_monitors(conn) position_limit_count = count_position_limit_active_monitors(conn)
opens_today = count_opens_for_trading_day(conn, trading_day) opens_today = count_opens_for_trading_day(conn, trading_day)
@@ -7414,7 +7415,7 @@ def api_price_snapshot():
pass pass
conn.close() conn.close()
from hub_position_metrics import build_position_marks_list from lib.hub.hub_position_metrics import build_position_marks_list
position_marks = build_position_marks_list( position_marks = build_position_marks_list(
all_swap_positions, all_swap_positions,
@@ -7647,7 +7648,7 @@ def api_order_kline():
"volume": float(bar[5]), "volume": float(bar[5]),
}) })
from focus_chart_lib import ( from lib.instance.focus_chart_lib import (
build_order_kline_order_payload, build_order_kline_order_payload,
load_swap_positions_for_order_kline, load_swap_positions_for_order_kline,
metrics_for_order_item, metrics_for_order_item,
@@ -7675,7 +7676,7 @@ def api_order_kline():
ex_metrics=ex_metrics, ex_metrics=ex_metrics,
) )
from focus_chart_lib import kline_api_price_fields from lib.instance.focus_chart_lib import kline_api_price_fields
price_fields = kline_api_price_fields( price_fields = kline_api_price_fields(
exchange, exchange,
@@ -7792,7 +7793,7 @@ def api_key_kline():
"lower_pct": lower_pct, "lower_pct": lower_pct,
} }
from focus_chart_lib import enrich_key_kline_response from lib.instance.focus_chart_lib import enrich_key_kline_response
price_display, key_info = enrich_key_kline_response( price_display, key_info = enrich_key_kline_response(
symbol=symbol, symbol=symbol,
@@ -7801,7 +7802,7 @@ def api_key_kline():
format_price_fn=format_price_for_symbol, format_price_fn=format_price_for_symbol,
) )
from focus_chart_lib import kline_api_price_fields from lib.instance.focus_chart_lib import kline_api_price_fields
price_fields = kline_api_price_fields( price_fields = kline_api_price_fields(
exchange, exchange,
@@ -8756,7 +8757,7 @@ def del_order(id):
opened_at=opened_at, opened_at=opened_at,
closed_at=closed_at, closed_at=closed_at,
) )
from account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close from lib.trade.account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close
on_user_initiated_close( on_user_initiated_close(
conn, conn,
@@ -8770,7 +8771,7 @@ def del_order(id):
try: try:
_rcfg = app.extensions.get("strategy_roll_cfg") _rcfg = app.extensions.get("strategy_roll_cfg")
if isinstance(_rcfg, dict): if isinstance(_rcfg, dict):
from strategy_register import roll_sync_after_external_close from lib.strategy.strategy_register import roll_sync_after_external_close
roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"]) roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"])
except Exception: except Exception:
@@ -8832,7 +8833,7 @@ def del_order(id):
opened_at=opened_at, opened_at=opened_at,
closed_at=closed_at, closed_at=closed_at,
) )
from account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close from lib.trade.account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close
on_user_initiated_close( on_user_initiated_close(
conn, conn,
@@ -8846,7 +8847,7 @@ def del_order(id):
try: try:
_rcfg = app.extensions.get("strategy_roll_cfg") _rcfg = app.extensions.get("strategy_roll_cfg")
if isinstance(_rcfg, dict): if isinstance(_rcfg, dict):
from strategy_register import roll_sync_after_external_close from lib.strategy.strategy_register import roll_sync_after_external_close
roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"]) roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"])
except Exception: except Exception:
@@ -9020,7 +9021,7 @@ def add_journal():
d.get("post_breakeven_stare"), d.get("new_trade_while_occupied"), d.get("note"), image_filename d.get("post_breakeven_stare"), d.get("new_trade_while_occupied"), d.get("note"), image_filename
) )
) )
from account_risk_lib import on_journal_saved from lib.trade.account_risk_lib import on_journal_saved
on_journal_saved( on_journal_saved(
conn, conn,
@@ -9108,7 +9109,7 @@ def api_reviews():
return jsonify([row_to_dict(r) for r in rows]) return jsonify([row_to_dict(r) for r in rows])
_REPO_STATIC_DIR = os.path.join(os.path.dirname(BASE_DIR), "static") _REPO_STATIC_DIR = common_static_dir(os.path.dirname(BASE_DIR))
_AI_REVIEW_RENDER_JS = os.path.join(_REPO_STATIC_DIR, "ai_review_render.js") _AI_REVIEW_RENDER_JS = os.path.join(_REPO_STATIC_DIR, "ai_review_render.js")
_FORM_SUBMIT_GUARD_JS = os.path.join(_REPO_STATIC_DIR, "form_submit_guard.js") _FORM_SUBMIT_GUARD_JS = os.path.join(_REPO_STATIC_DIR, "form_submit_guard.js")
_MANUAL_ORDER_RR_PREVIEW_JS = os.path.join(_REPO_STATIC_DIR, "manual_order_rr_preview.js") _MANUAL_ORDER_RR_PREVIEW_JS = os.path.join(_REPO_STATIC_DIR, "manual_order_rr_preview.js")
@@ -9342,7 +9343,7 @@ def api_trade_record_review_update():
tuple(base_params + [rec_id]), tuple(base_params + [rec_id]),
) )
if reviewed_result == "手动平仓" and reviewed_miss_reason: if reviewed_result == "手动平仓" and reviewed_miss_reason:
from account_risk_lib import apply_manual_close_journal_cooloff from lib.trade.account_risk_lib import apply_manual_close_journal_cooloff
apply_manual_close_journal_cooloff( apply_manual_close_journal_cooloff(
conn, conn,
@@ -9491,7 +9492,7 @@ def _hub_account_bundle():
def _hub_fetch_market(base=""): def _hub_fetch_market(base=""):
from hub_market_info_lib import fetch_usdt_swap_market_info from lib.hub.hub_market_info_lib import fetch_usdt_swap_market_info
return fetch_usdt_swap_market_info( return fetch_usdt_swap_market_info(
base_or_symbol=base, base_or_symbol=base,
@@ -9504,7 +9505,7 @@ def _hub_fetch_market(base=""):
def _hub_fetch_ohlcv(symbol, timeframe, since_ms=None, limit=500): def _hub_fetch_ohlcv(symbol, timeframe, since_ms=None, limit=500):
from hub_ohlcv_lib import fetch_ohlcv_for_hub from lib.hub.hub_ohlcv_lib import fetch_ohlcv_for_hub
return fetch_ohlcv_for_hub( return fetch_ohlcv_for_hub(
symbol=symbol, symbol=symbol,
@@ -9520,7 +9521,7 @@ def _hub_fetch_ohlcv(symbol, timeframe, since_ms=None, limit=500):
def _hub_fetch_volume_rank(top_n=20): def _hub_fetch_volume_rank(top_n=20):
from hub_volume_rank_lib import fetch_usdt_swap_volume_rank from lib.hub.hub_volume_rank_lib import fetch_usdt_swap_volume_rank
return fetch_usdt_swap_volume_rank( return fetch_usdt_swap_volume_rank(
exchange=exchange, exchange=exchange,
@@ -9537,7 +9538,7 @@ try:
_repo_root = Path(__file__).resolve().parent.parent _repo_root = Path(__file__).resolve().parent.parent
if str(_repo_root) not in sys.path: if str(_repo_root) not in sys.path:
sys.path.insert(0, str(_repo_root)) sys.path.insert(0, str(_repo_root))
from hub_bridge import install_on_app from lib.hub.hub_bridge import install_on_app
install_on_app( install_on_app(
app, app,
@@ -9581,8 +9582,8 @@ def strategy_roll_page():
return redirect("/strategy") return redirect("/strategy")
from strategy_register import install_strategy_trading from lib.strategy.strategy_register import install_strategy_trading
from strategy_trend_register import install_strategy_trend from lib.strategy.strategy_trend_register import install_strategy_trend
install_strategy_trading(app, _REPO_ROOT, app_module=sys.modules[__name__]) install_strategy_trading(app, _REPO_ROOT, app_module=sys.modules[__name__])
install_strategy_trend(app, _REPO_ROOT, app_module=sys.modules[__name__]) install_strategy_trend(app, _REPO_ROOT, app_module=sys.modules[__name__])
+65 -64
View File
@@ -34,14 +34,15 @@ import sys
if _REPO_ROOT not in sys.path: if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT) sys.path.insert(0, _REPO_ROOT)
from ai_client import ai_generate, ai_review, ai_short_advice from lib.paths import common_static_dir
from ai_review_lib import ( from lib.ai.ai_client import ai_generate, ai_review, ai_short_advice
from lib.ai.ai_review_lib import (
build_journal_ai_chart_path, build_journal_ai_chart_path,
collect_images_for_ai_review, collect_images_for_ai_review,
journal_row_lines_for_ai, journal_row_lines_for_ai,
) )
from form_submit_lib import check_duplicate_submit, submit_scope_add_key, submit_scope_add_order from lib.common.form_submit_lib import check_duplicate_submit, submit_scope_add_key, submit_scope_add_order
from fib_key_monitor_lib import ( from lib.key_monitor.fib_key_monitor_lib import (
FIB_KEY_MONITOR_TYPES, FIB_KEY_MONITOR_TYPES,
KEY_ENTRY_REASON_BY_SIGNAL, KEY_ENTRY_REASON_BY_SIGNAL,
backfill_missing_key_signal_types, backfill_missing_key_signal_types,
@@ -53,7 +54,7 @@ from fib_key_monitor_lib import (
key_signal_type_for_trade_record, key_signal_type_for_trade_record,
stored_key_signal_type, stored_key_signal_type,
) )
from false_breakout_key_monitor_lib import ( from lib.key_monitor.false_breakout_key_monitor_lib import (
FALSE_BREAKOUT_MONITOR_TYPE, FALSE_BREAKOUT_MONITOR_TYPE,
FALSE_BREAKOUT_VALIDITY_HOURS, FALSE_BREAKOUT_VALIDITY_HOURS,
calc_false_breakout_plan, calc_false_breakout_plan,
@@ -66,7 +67,7 @@ from false_breakout_key_monitor_lib import (
normalize_false_breakout_symbol, normalize_false_breakout_symbol,
storage_bounds_from_key_price, storage_bounds_from_key_price,
) )
from strategy_trade_labels import ( from lib.strategy.strategy_trade_labels import (
STRATEGY_ENTRY_REASON_OPTIONS, STRATEGY_ENTRY_REASON_OPTIONS,
apply_order_monitor_source_labels, apply_order_monitor_source_labels,
entry_reason_for_monitor_type, entry_reason_for_monitor_type,
@@ -75,7 +76,7 @@ from strategy_trade_labels import (
trade_record_monitor_type as resolve_trade_record_monitor_type, trade_record_monitor_type as resolve_trade_record_monitor_type,
trend_plan_id_from_monitor_row, trend_plan_id_from_monitor_row,
) )
from journal_chart_lib import ( from lib.instance.journal_chart_lib import (
JOURNAL_CHART_DEFAULT_LIMIT, JOURNAL_CHART_DEFAULT_LIMIT,
JOURNAL_CHART_DEFAULT_TF1, JOURNAL_CHART_DEFAULT_TF1,
JOURNAL_CHART_DEFAULT_TF2, JOURNAL_CHART_DEFAULT_TF2,
@@ -91,7 +92,7 @@ from journal_chart_lib import (
trade_review_fetch_window, trade_review_fetch_window,
trim_rows_for_trade_review, trim_rows_for_trade_review,
) )
from key_sl_tp_lib import ( from lib.key_monitor.key_sl_tp_lib import (
breakeven_enabled_from_row, breakeven_enabled_from_row,
normalize_sl_tp_mode, normalize_sl_tp_mode,
parse_breakeven_enabled_form, parse_breakeven_enabled_form,
@@ -100,7 +101,7 @@ from key_sl_tp_lib import (
sl_tp_mode_label, sl_tp_mode_label,
sl_tp_plan_summary_text, sl_tp_plan_summary_text,
) )
from time_close_lib import ( from lib.trade.time_close_lib import (
TIME_CLOSE_RESULT, TIME_CLOSE_RESULT,
apply_time_close_to_payload, apply_time_close_to_payload,
ensure_time_close_schema, ensure_time_close_schema,
@@ -111,13 +112,13 @@ from time_close_lib import (
time_close_label, time_close_label,
time_close_settings_from_row, time_close_settings_from_row,
) )
from manual_sltp_lib import ( from lib.trade.manual_sltp_lib import (
normalize_open_sltp_mode, normalize_open_sltp_mode,
resolve_entrust_sltp_prices, resolve_entrust_sltp_prices,
resolve_open_sltp_prices, resolve_open_sltp_prices,
) )
from key_monitor_schema_lib import ensure_key_monitor_schema from lib.key_monitor.key_monitor_schema_lib import ensure_key_monitor_schema
from trigger_entry_key_monitor_lib import ( from lib.key_monitor.trigger_entry_key_monitor_lib import (
BREAKOUT_TRIGGER_ENTRY_MONITOR_TYPE, BREAKOUT_TRIGGER_ENTRY_MONITOR_TYPE,
CALLBACK_TRIGGER_ENTRY_MONITOR_TYPE, CALLBACK_TRIGGER_ENTRY_MONITOR_TYPE,
TRIGGER_ENTRY_CLOSE_EXCHANGE_FAILED, TRIGGER_ENTRY_CLOSE_EXCHANGE_FAILED,
@@ -140,7 +141,7 @@ from trigger_entry_key_monitor_lib import (
validate_trigger_entry_geometry, validate_trigger_entry_geometry,
validate_trigger_entry_rr, validate_trigger_entry_rr,
) )
from position_sizing_lib import ( from lib.trade.position_sizing_lib import (
OPEN_SOURCE_KEY_AUTO, OPEN_SOURCE_KEY_AUTO,
OPEN_SOURCE_KEY_TRIGGER, OPEN_SOURCE_KEY_TRIGGER,
OPEN_SOURCE_MANUAL, OPEN_SOURCE_MANUAL,
@@ -154,12 +155,12 @@ from position_sizing_lib import (
mode_label_zh, mode_label_zh,
risk_percent_for_storage, risk_percent_for_storage,
) )
from key_monitor_full_margin_lib import ( from lib.key_monitor.key_monitor_full_margin_lib import (
monitor_type_disallowed_in_full_margin, monitor_type_disallowed_in_full_margin,
purge_disallowed_key_monitors, purge_disallowed_key_monitors,
) )
from auto_transfer_daily_lib import run_auto_transfer_once_per_day from lib.common.auto_transfer_daily_lib import run_auto_transfer_once_per_day
from key_monitor_lib import ( from lib.key_monitor.key_monitor_lib import (
KEY_DIRECTION_WATCH, KEY_DIRECTION_WATCH,
KEY_MONITOR_ALERT_ONLY_TYPES, KEY_MONITOR_ALERT_ONLY_TYPES,
KEY_MONITOR_AUTO_TYPES, KEY_MONITOR_AUTO_TYPES,
@@ -179,16 +180,16 @@ from key_monitor_lib import (
rs_break_from_direction, rs_break_from_direction,
run_rs_level_alert_tick, run_rs_level_alert_tick,
) )
from order_monitor_display_lib import ( from lib.trade.order_monitor_display_lib import (
apply_order_price_display_fields, apply_order_price_display_fields,
enrich_order_display_fields, enrich_order_display_fields,
order_monitor_tpsl_needs_sync, order_monitor_tpsl_needs_sync,
) )
from wechat_notify_lib import build_wechat_rs_level_message, send_wechat_webhook from lib.common.wechat_notify_lib import build_wechat_rs_level_message, send_wechat_webhook
from hub_auth import request_allowed as hub_request_allowed from lib.hub.hub_auth import request_allowed as hub_request_allowed
from instance_nav_lib import request_is_hub_soft_nav from lib.instance.instance_nav_lib import request_is_hub_soft_nav
from hub_volume_rank_lib import resolve_daily_volume_rank from lib.hub.hub_volume_rank_lib import resolve_daily_volume_rank
from history_window_lib import ( from lib.common.history_window_lib import (
PRESET_CUSTOM, PRESET_CUSTOM,
PRESET_UTC_LAST24H, PRESET_UTC_LAST24H,
PRESET_UTC_LAST7D, PRESET_UTC_LAST7D,
@@ -201,8 +202,8 @@ from history_window_lib import (
utc_window_to_bj_sql_strings, utc_window_to_bj_sql_strings,
utc_window_to_utc_sql_strings, utc_window_to_utc_sql_strings,
) )
from trade_result_lib import count_winning_trades, normalize_result_with_pnl from lib.trade.trade_result_lib import count_winning_trades, normalize_result_with_pnl
from trade_exchange_stats_lib import attach_exchange_stats_to_trade, filter_position_lifecycle_fills from lib.trade.trade_exchange_stats_lib import attach_exchange_stats_to_trade, filter_position_lifecycle_fills
def load_env_file(path): def load_env_file(path):
@@ -343,7 +344,7 @@ ORDER_CHART_ENABLED = os.getenv("ORDER_CHART_ENABLED", "true").lower() == "true"
ORDER_CHART_TFS = [x.strip() for x in (os.getenv("ORDER_CHART_TFS", "4h,1h,15m,5m") or "").split(",") if x.strip()] ORDER_CHART_TFS = [x.strip() for x in (os.getenv("ORDER_CHART_TFS", "4h,1h,15m,5m") or "").split(",") if x.strip()]
ORDER_CHART_LIMIT = int(os.getenv("ORDER_CHART_LIMIT", "100")) ORDER_CHART_LIMIT = int(os.getenv("ORDER_CHART_LIMIT", "100"))
ORDER_CHART_DIR = resolve_path(os.getenv("ORDER_CHART_DIR", "static/images/order_charts")) ORDER_CHART_DIR = resolve_path(os.getenv("ORDER_CHART_DIR", "static/images/order_charts"))
from daily_open_limit_lib import ( from lib.trade.daily_open_limit_lib import (
build_daily_open_alert_prompt, build_daily_open_alert_prompt,
can_trade_new_open, can_trade_new_open,
check_daily_open_hard_limit, check_daily_open_hard_limit,
@@ -1506,10 +1507,10 @@ def init_db():
close_reason TEXT, closed_at TEXT)""" close_reason TEXT, closed_at TEXT)"""
) )
from strategy_db import init_strategy_tables from lib.strategy.strategy_db import init_strategy_tables
init_strategy_tables(conn) init_strategy_tables(conn)
from account_risk_lib import ensure_account_risk_schema from lib.trade.account_risk_lib import ensure_account_risk_schema
ensure_account_risk_schema(conn) ensure_account_risk_schema(conn)
backfill_missing_key_signal_types(conn, monitor_type=ORDER_MONITOR_TYPE_KEY_AUTO) backfill_missing_key_signal_types(conn, monitor_type=ORDER_MONITOR_TYPE_KEY_AUTO)
@@ -1546,7 +1547,7 @@ def get_db():
def hub_account_risk_status(conn): def hub_account_risk_status(conn):
from account_risk_lib import ( from lib.trade.account_risk_lib import (
apply_position_limit_risk, apply_position_limit_risk,
compute_account_risk_status, compute_account_risk_status,
enrich_risk_status_countdown, enrich_risk_status_countdown,
@@ -1562,7 +1563,7 @@ def hub_account_risk_status(conn):
fmt_local_ms=ms_to_app_local_str, fmt_local_ms=ms_to_app_local_str,
) )
st = enrich_risk_status_countdown(st, now=now, daily_reset_hour=TRADING_DAY_RESET_HOUR) st = enrich_risk_status_countdown(st, now=now, daily_reset_hour=TRADING_DAY_RESET_HOUR)
from strategy_trade_labels import count_position_limit_active_monitors from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
return apply_position_limit_risk( return apply_position_limit_risk(
st, st,
@@ -1579,7 +1580,7 @@ def hub_user_initiated_close(
trade_record_id=None, trade_record_id=None,
closed_at_ms=None, closed_at_ms=None,
): ):
from account_risk_lib import CLOSE_SOURCE_USER_HUB, on_user_initiated_close from lib.trade.account_risk_lib import CLOSE_SOURCE_USER_HUB, on_user_initiated_close
src = (source or "").strip() or CLOSE_SOURCE_USER_HUB src = (source or "").strip() or CLOSE_SOURCE_USER_HUB
on_user_initiated_close( on_user_initiated_close(
@@ -2072,7 +2073,7 @@ def get_effective_trade_field(row, reviewed_key, base_key, default=None):
def to_effective_trade_dict(row): def to_effective_trade_dict(row):
item = row_to_dict(row) item = row_to_dict(row)
from order_monitor_display_lib import snapshot_stop_loss from lib.trade.order_monitor_display_lib import snapshot_stop_loss
open_stop = snapshot_stop_loss(item.get("initial_stop_loss"), item.get("stop_loss")) open_stop = snapshot_stop_loss(item.get("initial_stop_loss"), item.get("stop_loss"))
item["display_open_stop_loss"] = open_stop item["display_open_stop_loss"] = open_stop
@@ -2370,7 +2371,7 @@ def insert_trade_record(
open_ts_ms = _to_ms_with_fallback(opened_at_ms, open_ts) open_ts_ms = _to_ms_with_fallback(opened_at_ms, open_ts)
close_ts_ms = _to_ms_with_fallback(closed_at_ms, close_ts) close_ts_ms = _to_ms_with_fallback(closed_at_ms, close_ts)
kst = key_signal_type_for_trade_record(key_signal_type, KEY_MONITOR_AUTO_TYPES) kst = key_signal_type_for_trade_record(key_signal_type, KEY_MONITOR_AUTO_TYPES)
from order_monitor_display_lib import snapshot_stop_loss from lib.trade.order_monitor_display_lib import snapshot_stop_loss
snap_sl = snapshot_stop_loss(initial_stop_loss, stop_loss) snap_sl = snapshot_stop_loss(initial_stop_loss, stop_loss)
er = ( er = (
@@ -2761,7 +2762,7 @@ def get_exchange_capitals(force=False):
def execute_transfer_usdt(amount, from_account, to_account): def execute_transfer_usdt(amount, from_account, to_account):
from gate_transfer_lib import execute_transfer_usdt as _gate_execute_transfer_usdt from lib.exchange.gate_transfer_lib import execute_transfer_usdt as _gate_execute_transfer_usdt
return _gate_execute_transfer_usdt( return _gate_execute_transfer_usdt(
exchange, exchange,
@@ -2794,7 +2795,7 @@ def get_account_usdt_total(account_type):
def _auto_transfer_active_count(conn): def _auto_transfer_active_count(conn):
from gate_transfer_lib import count_auto_transfer_blockers from lib.exchange.gate_transfer_lib import count_auto_transfer_blockers
return count_auto_transfer_blockers(conn, count_order_monitors=get_active_position_count) return count_auto_transfer_blockers(conn, count_order_monitors=get_active_position_count)
@@ -2878,7 +2879,7 @@ def resolve_capital_base_for_key_open(conn, trading_day, live_capital):
def precheck_risk(conn, symbol, direction): def precheck_risk(conn, symbol, direction):
now = app_now() now = app_now()
from account_risk_lib import account_risk_blocks_trading from lib.trade.account_risk_lib import account_risk_blocks_trading
ok_risk, risk_reason = account_risk_blocks_trading( ok_risk, risk_reason = account_risk_blocks_trading(
conn, conn,
@@ -2890,7 +2891,7 @@ def precheck_risk(conn, symbol, direction):
return False, risk_reason return False, risk_reason
if not trading_day_reset_allows_new_open(now): if not trading_day_reset_allows_new_open(now):
return False, f"北京时间 {TRADING_DAY_RESET_HOUR}:00 前不允许持仓" return False, f"北京时间 {TRADING_DAY_RESET_HOUR}:00 前不允许持仓"
from account_risk_lib import position_limit_reached from lib.trade.account_risk_lib import position_limit_reached
reached, active_count, mx = position_limit_reached(conn, max_active_positions=MAX_ACTIVE_POSITIONS) reached, active_count, mx = position_limit_reached(conn, max_active_positions=MAX_ACTIVE_POSITIONS)
if reached: if reached:
@@ -3670,7 +3671,7 @@ def parse_ccxt_position_metrics(position, order_leverage=None):
cs = float(get_contract_size(sym)) if sym else 1.0 cs = float(get_contract_size(sym)) if sym else 1.0
except Exception: except Exception:
cs = 1.0 cs = 1.0
from hub_position_metrics import enrich_ccxt_position_metrics_out from lib.hub.hub_position_metrics import enrich_ccxt_position_metrics_out
enrich_ccxt_position_metrics_out(p, out, contract_size=cs, funds_decimals=2) enrich_ccxt_position_metrics_out(p, out, contract_size=cs, funds_decimals=2)
return out or None return out or None
@@ -3854,7 +3855,7 @@ def fetch_latest_closing_fill(exchange_symbol, direction, opened_at_str, opened_
except Exception: except Exception:
pass pass
try: try:
from gate_position_history_lib import pick_gate_position_close from lib.exchange.gate_position_history_lib import pick_gate_position_close
pos = pick_gate_position_close( pos = pick_gate_position_close(
fetch_gate_positions_close_history(), fetch_gate_positions_close_history(),
@@ -4114,7 +4115,7 @@ def reconcile_hub_external_close(conn, symbol, direction):
"""中控市价全平后:立即同步匹配 order_monitor,并读 Gate 平仓历史。""" """中控市价全平后:立即同步匹配 order_monitor,并读 Gate 平仓历史。"""
if not exchange_private_api_configured(): if not exchange_private_api_configured():
return {"ok": False, "msg": "未配置 GATE_API_KEY / GATE_API_SECRET", "synced": 0} return {"ok": False, "msg": "未配置 GATE_API_KEY / GATE_API_SECRET", "synced": 0}
from gate_position_history_lib import unified_symbol_for_match from lib.exchange.gate_position_history_lib import unified_symbol_for_match
sym_u = unified_symbol_for_match(symbol) sym_u = unified_symbol_for_match(symbol)
dir_l = (direction or "").strip().lower() dir_l = (direction or "").strip().lower()
@@ -6513,14 +6514,14 @@ def background_task():
check_trigger_entry_key_monitors() check_trigger_entry_key_monitors()
_roll_cfg = app.extensions.get("strategy_roll_cfg") _roll_cfg = app.extensions.get("strategy_roll_cfg")
if _roll_cfg: if _roll_cfg:
from strategy_roll_monitor_lib import check_roll_monitors from lib.strategy.strategy_roll_monitor_lib import check_roll_monitors
check_roll_monitors(_roll_cfg) check_roll_monitors(_roll_cfg)
check_key_monitors() check_key_monitors()
check_order_monitors() check_order_monitors()
cfg = app.extensions.get("strategy_trend_cfg") cfg = app.extensions.get("strategy_trend_cfg")
if cfg: if cfg:
from strategy_trend_register import check_trend_pullback_plans from lib.strategy.strategy_trend_register import check_trend_pullback_plans
check_trend_pullback_plans(cfg) check_trend_pullback_plans(cfg)
except: except:
@@ -6848,7 +6849,7 @@ def render_main_page(page="trade", embed_mode=None):
conn = get_db() conn = get_db()
session_row = ensure_session(conn, trading_day) session_row = ensure_session(conn, trading_day)
local_current_capital = float(session_row["current_capital"]) local_current_capital = float(session_row["current_capital"])
from instance_embed_context_lib import ( from lib.instance.instance_embed_context_lib import (
embed_render_plan, embed_render_plan,
minimal_stats_bundle, minimal_stats_bundle,
trade_records_summary, trade_records_summary,
@@ -6921,7 +6922,7 @@ def render_main_page(page="trade", embed_mode=None):
records = [] records = []
total = miss_count = rate = occupied_miss_total = 0 total = miss_count = rate = occupied_miss_total = 0
active_count = len(order_list) active_count = len(order_list)
from strategy_trade_labels import count_position_limit_active_monitors from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
position_limit_count = count_position_limit_active_monitors(conn) position_limit_count = count_position_limit_active_monitors(conn)
opens_today = count_opens_for_trading_day(conn, trading_day) opens_today = count_opens_for_trading_day(conn, trading_day)
@@ -6952,7 +6953,7 @@ def render_main_page(page="trade", embed_mode=None):
) )
strategy_extra = {} strategy_extra = {}
if plan.strategy: if plan.strategy:
from strategy_ui import strategy_render_extras from lib.strategy.strategy_ui import strategy_render_extras
strategy_extra = strategy_render_extras( strategy_extra = strategy_render_extras(
conn, conn,
@@ -6962,7 +6963,7 @@ def render_main_page(page="trade", embed_mode=None):
trend_cfg=app.extensions.get("strategy_trend_cfg"), trend_cfg=app.extensions.get("strategy_trend_cfg"),
) )
conn.close() conn.close()
from instance_embed_lib import embed_context_extras from lib.instance.instance_embed_lib import embed_context_extras
template_ctx = dict( template_ctx = dict(
page=page, page=page,
@@ -7100,7 +7101,7 @@ def api_account_snapshot():
funding_usdt = round(funding_capital, 2) if funding_capital is not None else None funding_usdt = round(funding_capital, 2) if funding_capital is not None else None
current_capital = round(trading_capital, 2) if trading_capital is not None else round(local_current_capital, 2) current_capital = round(trading_capital, 2) if trading_capital is not None else round(local_current_capital, 2)
recommended_capital = round(float(get_recommended_capital(current_capital)), 2) recommended_capital = round(float(get_recommended_capital(current_capital)), 2)
from strategy_trade_labels import count_position_limit_active_monitors from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
position_limit_count = count_position_limit_active_monitors(conn) position_limit_count = count_position_limit_active_monitors(conn)
opens_today = count_opens_for_trading_day(conn, trading_day) opens_today = count_opens_for_trading_day(conn, trading_day)
@@ -7410,7 +7411,7 @@ def api_price_snapshot():
pass pass
conn.close() conn.close()
from hub_position_metrics import build_position_marks_list from lib.hub.hub_position_metrics import build_position_marks_list
position_marks = build_position_marks_list( position_marks = build_position_marks_list(
all_swap_positions, all_swap_positions,
@@ -7643,7 +7644,7 @@ def api_order_kline():
"volume": float(bar[5]), "volume": float(bar[5]),
}) })
from focus_chart_lib import ( from lib.instance.focus_chart_lib import (
build_order_kline_order_payload, build_order_kline_order_payload,
load_swap_positions_for_order_kline, load_swap_positions_for_order_kline,
metrics_for_order_item, metrics_for_order_item,
@@ -7671,7 +7672,7 @@ def api_order_kline():
ex_metrics=ex_metrics, ex_metrics=ex_metrics,
) )
from focus_chart_lib import kline_api_price_fields from lib.instance.focus_chart_lib import kline_api_price_fields
price_fields = kline_api_price_fields( price_fields = kline_api_price_fields(
exchange, exchange,
@@ -7788,7 +7789,7 @@ def api_key_kline():
"lower_pct": lower_pct, "lower_pct": lower_pct,
} }
from focus_chart_lib import enrich_key_kline_response from lib.instance.focus_chart_lib import enrich_key_kline_response
price_display, key_info = enrich_key_kline_response( price_display, key_info = enrich_key_kline_response(
symbol=symbol, symbol=symbol,
@@ -7797,7 +7798,7 @@ def api_key_kline():
format_price_fn=format_price_for_symbol, format_price_fn=format_price_for_symbol,
) )
from focus_chart_lib import kline_api_price_fields from lib.instance.focus_chart_lib import kline_api_price_fields
price_fields = kline_api_price_fields( price_fields = kline_api_price_fields(
exchange, exchange,
@@ -8752,7 +8753,7 @@ def del_order(id):
opened_at=opened_at, opened_at=opened_at,
closed_at=closed_at, closed_at=closed_at,
) )
from account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close from lib.trade.account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close
on_user_initiated_close( on_user_initiated_close(
conn, conn,
@@ -8766,7 +8767,7 @@ def del_order(id):
try: try:
_rcfg = app.extensions.get("strategy_roll_cfg") _rcfg = app.extensions.get("strategy_roll_cfg")
if isinstance(_rcfg, dict): if isinstance(_rcfg, dict):
from strategy_register import roll_sync_after_external_close from lib.strategy.strategy_register import roll_sync_after_external_close
roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"]) roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"])
except Exception: except Exception:
@@ -8828,7 +8829,7 @@ def del_order(id):
opened_at=opened_at, opened_at=opened_at,
closed_at=closed_at, closed_at=closed_at,
) )
from account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close from lib.trade.account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close
on_user_initiated_close( on_user_initiated_close(
conn, conn,
@@ -8842,7 +8843,7 @@ def del_order(id):
try: try:
_rcfg = app.extensions.get("strategy_roll_cfg") _rcfg = app.extensions.get("strategy_roll_cfg")
if isinstance(_rcfg, dict): if isinstance(_rcfg, dict):
from strategy_register import roll_sync_after_external_close from lib.strategy.strategy_register import roll_sync_after_external_close
roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"]) roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"])
except Exception: except Exception:
@@ -9016,7 +9017,7 @@ def add_journal():
d.get("post_breakeven_stare"), d.get("new_trade_while_occupied"), d.get("note"), image_filename d.get("post_breakeven_stare"), d.get("new_trade_while_occupied"), d.get("note"), image_filename
) )
) )
from account_risk_lib import on_journal_saved from lib.trade.account_risk_lib import on_journal_saved
on_journal_saved( on_journal_saved(
conn, conn,
@@ -9104,7 +9105,7 @@ def api_reviews():
return jsonify([row_to_dict(r) for r in rows]) return jsonify([row_to_dict(r) for r in rows])
_REPO_STATIC_DIR = os.path.join(os.path.dirname(BASE_DIR), "static") _REPO_STATIC_DIR = common_static_dir(os.path.dirname(BASE_DIR))
_AI_REVIEW_RENDER_JS = os.path.join(_REPO_STATIC_DIR, "ai_review_render.js") _AI_REVIEW_RENDER_JS = os.path.join(_REPO_STATIC_DIR, "ai_review_render.js")
_FORM_SUBMIT_GUARD_JS = os.path.join(_REPO_STATIC_DIR, "form_submit_guard.js") _FORM_SUBMIT_GUARD_JS = os.path.join(_REPO_STATIC_DIR, "form_submit_guard.js")
_MANUAL_ORDER_RR_PREVIEW_JS = os.path.join(_REPO_STATIC_DIR, "manual_order_rr_preview.js") _MANUAL_ORDER_RR_PREVIEW_JS = os.path.join(_REPO_STATIC_DIR, "manual_order_rr_preview.js")
@@ -9338,7 +9339,7 @@ def api_trade_record_review_update():
tuple(base_params + [rec_id]), tuple(base_params + [rec_id]),
) )
if reviewed_result == "手动平仓" and reviewed_miss_reason: if reviewed_result == "手动平仓" and reviewed_miss_reason:
from account_risk_lib import apply_manual_close_journal_cooloff from lib.trade.account_risk_lib import apply_manual_close_journal_cooloff
apply_manual_close_journal_cooloff( apply_manual_close_journal_cooloff(
conn, conn,
@@ -9487,7 +9488,7 @@ def _hub_account_bundle():
def _hub_fetch_market(base=""): def _hub_fetch_market(base=""):
from hub_market_info_lib import fetch_usdt_swap_market_info from lib.hub.hub_market_info_lib import fetch_usdt_swap_market_info
return fetch_usdt_swap_market_info( return fetch_usdt_swap_market_info(
base_or_symbol=base, base_or_symbol=base,
@@ -9500,7 +9501,7 @@ def _hub_fetch_market(base=""):
def _hub_fetch_ohlcv(symbol, timeframe, since_ms=None, limit=500): def _hub_fetch_ohlcv(symbol, timeframe, since_ms=None, limit=500):
from hub_ohlcv_lib import fetch_ohlcv_for_hub from lib.hub.hub_ohlcv_lib import fetch_ohlcv_for_hub
return fetch_ohlcv_for_hub( return fetch_ohlcv_for_hub(
symbol=symbol, symbol=symbol,
@@ -9516,7 +9517,7 @@ def _hub_fetch_ohlcv(symbol, timeframe, since_ms=None, limit=500):
def _hub_fetch_volume_rank(top_n=20): def _hub_fetch_volume_rank(top_n=20):
from hub_volume_rank_lib import fetch_usdt_swap_volume_rank from lib.hub.hub_volume_rank_lib import fetch_usdt_swap_volume_rank
return fetch_usdt_swap_volume_rank( return fetch_usdt_swap_volume_rank(
exchange=exchange, exchange=exchange,
@@ -9533,7 +9534,7 @@ try:
_repo_root = Path(__file__).resolve().parent.parent _repo_root = Path(__file__).resolve().parent.parent
if str(_repo_root) not in sys.path: if str(_repo_root) not in sys.path:
sys.path.insert(0, str(_repo_root)) sys.path.insert(0, str(_repo_root))
from hub_bridge import install_on_app from lib.hub.hub_bridge import install_on_app
install_on_app( install_on_app(
app, app,
@@ -9577,8 +9578,8 @@ def strategy_roll_page():
return redirect("/strategy") return redirect("/strategy")
from strategy_register import install_strategy_trading from lib.strategy.strategy_register import install_strategy_trading
from strategy_trend_register import install_strategy_trend from lib.strategy.strategy_trend_register import install_strategy_trend
install_strategy_trading(app, _REPO_ROOT, app_module=sys.modules[__name__]) install_strategy_trading(app, _REPO_ROOT, app_module=sys.modules[__name__])
install_strategy_trend(app, _REPO_ROOT, app_module=sys.modules[__name__]) install_strategy_trend(app, _REPO_ROOT, app_module=sys.modules[__name__])
+63 -62
View File
@@ -34,14 +34,15 @@ import sys
if _REPO_ROOT not in sys.path: if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT) sys.path.insert(0, _REPO_ROOT)
from ai_client import ai_generate, ai_review, ai_short_advice from lib.paths import common_static_dir
from ai_review_lib import ( from lib.ai.ai_client import ai_generate, ai_review, ai_short_advice
from lib.ai.ai_review_lib import (
build_journal_ai_chart_path, build_journal_ai_chart_path,
collect_images_for_ai_review, collect_images_for_ai_review,
journal_row_lines_for_ai, journal_row_lines_for_ai,
) )
from form_submit_lib import check_duplicate_submit, submit_scope_add_key, submit_scope_add_order from lib.common.form_submit_lib import check_duplicate_submit, submit_scope_add_key, submit_scope_add_order
from fib_key_monitor_lib import ( from lib.key_monitor.fib_key_monitor_lib import (
FIB_KEY_MONITOR_TYPES, FIB_KEY_MONITOR_TYPES,
backfill_missing_key_signal_types, backfill_missing_key_signal_types,
calc_fib_plan, calc_fib_plan,
@@ -52,7 +53,7 @@ from fib_key_monitor_lib import (
key_signal_type_for_trade_record, key_signal_type_for_trade_record,
stored_key_signal_type, stored_key_signal_type,
) )
from false_breakout_key_monitor_lib import ( from lib.key_monitor.false_breakout_key_monitor_lib import (
FALSE_BREAKOUT_MONITOR_TYPE, FALSE_BREAKOUT_MONITOR_TYPE,
FALSE_BREAKOUT_VALIDITY_HOURS, FALSE_BREAKOUT_VALIDITY_HOURS,
calc_false_breakout_plan, calc_false_breakout_plan,
@@ -65,7 +66,7 @@ from false_breakout_key_monitor_lib import (
normalize_false_breakout_symbol, normalize_false_breakout_symbol,
storage_bounds_from_key_price, storage_bounds_from_key_price,
) )
from strategy_trade_labels import ( from lib.strategy.strategy_trade_labels import (
STRATEGY_ENTRY_REASON_OPTIONS, STRATEGY_ENTRY_REASON_OPTIONS,
apply_order_monitor_source_labels, apply_order_monitor_source_labels,
entry_reason_for_monitor_type, entry_reason_for_monitor_type,
@@ -74,8 +75,8 @@ from strategy_trade_labels import (
trade_record_monitor_type as resolve_trade_record_monitor_type, trade_record_monitor_type as resolve_trade_record_monitor_type,
trend_plan_id_from_monitor_row, trend_plan_id_from_monitor_row,
) )
from okx_orders_lib import cancel_okx_all_open_orders, fetch_okx_all_open_orders from lib.exchange.okx_orders_lib import cancel_okx_all_open_orders, fetch_okx_all_open_orders
from journal_chart_lib import ( from lib.instance.journal_chart_lib import (
JOURNAL_CHART_DEFAULT_LIMIT, JOURNAL_CHART_DEFAULT_LIMIT,
JOURNAL_CHART_DEFAULT_TF1, JOURNAL_CHART_DEFAULT_TF1,
JOURNAL_CHART_DEFAULT_TF2, JOURNAL_CHART_DEFAULT_TF2,
@@ -91,7 +92,7 @@ from journal_chart_lib import (
trade_review_fetch_window, trade_review_fetch_window,
trim_rows_for_trade_review, trim_rows_for_trade_review,
) )
from key_sl_tp_lib import ( from lib.key_monitor.key_sl_tp_lib import (
breakeven_enabled_from_row, breakeven_enabled_from_row,
normalize_sl_tp_mode, normalize_sl_tp_mode,
parse_breakeven_enabled_form, parse_breakeven_enabled_form,
@@ -100,7 +101,7 @@ from key_sl_tp_lib import (
sl_tp_mode_label, sl_tp_mode_label,
sl_tp_plan_summary_text, sl_tp_plan_summary_text,
) )
from time_close_lib import ( from lib.trade.time_close_lib import (
TIME_CLOSE_RESULT, TIME_CLOSE_RESULT,
apply_time_close_to_payload, apply_time_close_to_payload,
ensure_time_close_schema, ensure_time_close_schema,
@@ -111,13 +112,13 @@ from time_close_lib import (
time_close_label, time_close_label,
time_close_settings_from_row, time_close_settings_from_row,
) )
from manual_sltp_lib import ( from lib.trade.manual_sltp_lib import (
normalize_open_sltp_mode, normalize_open_sltp_mode,
resolve_entrust_sltp_prices, resolve_entrust_sltp_prices,
resolve_open_sltp_prices, resolve_open_sltp_prices,
) )
from key_monitor_schema_lib import ensure_key_monitor_schema from lib.key_monitor.key_monitor_schema_lib import ensure_key_monitor_schema
from trigger_entry_key_monitor_lib import ( from lib.key_monitor.trigger_entry_key_monitor_lib import (
BREAKOUT_TRIGGER_ENTRY_MONITOR_TYPE, BREAKOUT_TRIGGER_ENTRY_MONITOR_TYPE,
CALLBACK_TRIGGER_ENTRY_MONITOR_TYPE, CALLBACK_TRIGGER_ENTRY_MONITOR_TYPE,
TRIGGER_ENTRY_CLOSE_EXCHANGE_FAILED, TRIGGER_ENTRY_CLOSE_EXCHANGE_FAILED,
@@ -140,7 +141,7 @@ from trigger_entry_key_monitor_lib import (
validate_trigger_entry_geometry, validate_trigger_entry_geometry,
validate_trigger_entry_rr, validate_trigger_entry_rr,
) )
from position_sizing_lib import ( from lib.trade.position_sizing_lib import (
OPEN_SOURCE_KEY_AUTO, OPEN_SOURCE_KEY_AUTO,
OPEN_SOURCE_MANUAL, OPEN_SOURCE_MANUAL,
assert_open_source_allowed, assert_open_source_allowed,
@@ -153,12 +154,12 @@ from position_sizing_lib import (
mode_label_zh, mode_label_zh,
risk_percent_for_storage, risk_percent_for_storage,
) )
from key_monitor_full_margin_lib import ( from lib.key_monitor.key_monitor_full_margin_lib import (
monitor_type_disallowed_in_full_margin, monitor_type_disallowed_in_full_margin,
purge_disallowed_key_monitors, purge_disallowed_key_monitors,
) )
from auto_transfer_daily_lib import run_auto_transfer_once_per_day from lib.common.auto_transfer_daily_lib import run_auto_transfer_once_per_day
from key_monitor_lib import ( from lib.key_monitor.key_monitor_lib import (
KEY_DIRECTION_WATCH, KEY_DIRECTION_WATCH,
KEY_MONITOR_ALERT_ONLY_TYPES, KEY_MONITOR_ALERT_ONLY_TYPES,
KEY_MONITOR_AUTO_TYPES, KEY_MONITOR_AUTO_TYPES,
@@ -178,16 +179,16 @@ from key_monitor_lib import (
rs_break_from_direction, rs_break_from_direction,
run_rs_level_alert_tick, run_rs_level_alert_tick,
) )
from order_monitor_display_lib import ( from lib.trade.order_monitor_display_lib import (
apply_order_price_display_fields, apply_order_price_display_fields,
enrich_order_display_fields, enrich_order_display_fields,
order_monitor_tpsl_needs_sync, order_monitor_tpsl_needs_sync,
) )
from wechat_notify_lib import build_wechat_rs_level_message, send_wechat_webhook from lib.common.wechat_notify_lib import build_wechat_rs_level_message, send_wechat_webhook
from hub_auth import request_allowed as hub_request_allowed from lib.hub.hub_auth import request_allowed as hub_request_allowed
from instance_nav_lib import request_is_hub_soft_nav from lib.instance.instance_nav_lib import request_is_hub_soft_nav
from hub_volume_rank_lib import resolve_daily_volume_rank from lib.hub.hub_volume_rank_lib import resolve_daily_volume_rank
from history_window_lib import ( from lib.common.history_window_lib import (
PRESET_CUSTOM, PRESET_CUSTOM,
PRESET_UTC_LAST24H, PRESET_UTC_LAST24H,
PRESET_UTC_LAST7D, PRESET_UTC_LAST7D,
@@ -200,8 +201,8 @@ from history_window_lib import (
utc_window_to_bj_sql_strings, utc_window_to_bj_sql_strings,
utc_window_to_utc_sql_strings, utc_window_to_utc_sql_strings,
) )
from trade_result_lib import count_winning_trades, normalize_result_with_pnl from lib.trade.trade_result_lib import count_winning_trades, normalize_result_with_pnl
from trade_exchange_stats_lib import attach_exchange_stats_to_trade, filter_position_lifecycle_fills from lib.trade.trade_exchange_stats_lib import attach_exchange_stats_to_trade, filter_position_lifecycle_fills
def load_env_file(path): def load_env_file(path):
@@ -323,7 +324,7 @@ ORDER_CHART_ENABLED = os.getenv("ORDER_CHART_ENABLED", "true").lower() == "true"
ORDER_CHART_TFS = [x.strip() for x in (os.getenv("ORDER_CHART_TFS", "4h,1h,15m,5m") or "").split(",") if x.strip()] ORDER_CHART_TFS = [x.strip() for x in (os.getenv("ORDER_CHART_TFS", "4h,1h,15m,5m") or "").split(",") if x.strip()]
ORDER_CHART_LIMIT = int(os.getenv("ORDER_CHART_LIMIT", "100")) ORDER_CHART_LIMIT = int(os.getenv("ORDER_CHART_LIMIT", "100"))
ORDER_CHART_DIR = resolve_path(os.getenv("ORDER_CHART_DIR", "static/images/order_charts")) ORDER_CHART_DIR = resolve_path(os.getenv("ORDER_CHART_DIR", "static/images/order_charts"))
from daily_open_limit_lib import ( from lib.trade.daily_open_limit_lib import (
build_daily_open_alert_prompt, build_daily_open_alert_prompt,
can_trade_new_open, can_trade_new_open,
check_daily_open_hard_limit, check_daily_open_hard_limit,
@@ -1493,10 +1494,10 @@ def init_db():
close_reason TEXT, closed_at TEXT)""" close_reason TEXT, closed_at TEXT)"""
) )
from strategy_db import init_strategy_tables from lib.strategy.strategy_db import init_strategy_tables
init_strategy_tables(conn) init_strategy_tables(conn)
from account_risk_lib import ensure_account_risk_schema from lib.trade.account_risk_lib import ensure_account_risk_schema
ensure_account_risk_schema(conn) ensure_account_risk_schema(conn)
backfill_missing_key_signal_types(conn, monitor_type=ORDER_MONITOR_TYPE_KEY_AUTO) backfill_missing_key_signal_types(conn, monitor_type=ORDER_MONITOR_TYPE_KEY_AUTO)
@@ -1533,7 +1534,7 @@ def get_db():
def hub_account_risk_status(conn): def hub_account_risk_status(conn):
from account_risk_lib import ( from lib.trade.account_risk_lib import (
apply_position_limit_risk, apply_position_limit_risk,
compute_account_risk_status, compute_account_risk_status,
enrich_risk_status_countdown, enrich_risk_status_countdown,
@@ -1549,7 +1550,7 @@ def hub_account_risk_status(conn):
fmt_local_ms=ms_to_app_local_str, fmt_local_ms=ms_to_app_local_str,
) )
st = enrich_risk_status_countdown(st, now=now, daily_reset_hour=TRADING_DAY_RESET_HOUR) st = enrich_risk_status_countdown(st, now=now, daily_reset_hour=TRADING_DAY_RESET_HOUR)
from strategy_trade_labels import count_position_limit_active_monitors from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
return apply_position_limit_risk( return apply_position_limit_risk(
st, st,
@@ -1566,7 +1567,7 @@ def hub_user_initiated_close(
trade_record_id=None, trade_record_id=None,
closed_at_ms=None, closed_at_ms=None,
): ):
from account_risk_lib import CLOSE_SOURCE_USER_HUB, on_user_initiated_close from lib.trade.account_risk_lib import CLOSE_SOURCE_USER_HUB, on_user_initiated_close
src = (source or "").strip() or CLOSE_SOURCE_USER_HUB src = (source or "").strip() or CLOSE_SOURCE_USER_HUB
on_user_initiated_close( on_user_initiated_close(
@@ -2021,7 +2022,7 @@ def get_effective_trade_field(row, reviewed_key, base_key, default=None):
def to_effective_trade_dict(row): def to_effective_trade_dict(row):
item = row_to_dict(row) item = row_to_dict(row)
from order_monitor_display_lib import snapshot_stop_loss from lib.trade.order_monitor_display_lib import snapshot_stop_loss
open_stop = snapshot_stop_loss(item.get("initial_stop_loss"), item.get("stop_loss")) open_stop = snapshot_stop_loss(item.get("initial_stop_loss"), item.get("stop_loss"))
item["display_open_stop_loss"] = open_stop item["display_open_stop_loss"] = open_stop
@@ -2266,7 +2267,7 @@ def insert_trade_record(
open_ts_ms = _to_ms_with_fallback(opened_at_ms, open_ts) open_ts_ms = _to_ms_with_fallback(opened_at_ms, open_ts)
close_ts_ms = _to_ms_with_fallback(closed_at_ms, close_ts) close_ts_ms = _to_ms_with_fallback(closed_at_ms, close_ts)
kst = key_signal_type_for_trade_record(key_signal_type, KEY_MONITOR_AUTO_TYPES) kst = key_signal_type_for_trade_record(key_signal_type, KEY_MONITOR_AUTO_TYPES)
from order_monitor_display_lib import snapshot_stop_loss from lib.trade.order_monitor_display_lib import snapshot_stop_loss
snap_sl = snapshot_stop_loss(initial_stop_loss, stop_loss) snap_sl = snapshot_stop_loss(initial_stop_loss, stop_loss)
er = ( er = (
@@ -2590,7 +2591,7 @@ def trading_day_reset_allows_new_open(now, conn=None):
def precheck_risk(conn, symbol, direction): def precheck_risk(conn, symbol, direction):
now = app_now() now = app_now()
from account_risk_lib import account_risk_blocks_trading from lib.trade.account_risk_lib import account_risk_blocks_trading
ok_risk, risk_reason = account_risk_blocks_trading( ok_risk, risk_reason = account_risk_blocks_trading(
conn, conn,
@@ -2602,7 +2603,7 @@ def precheck_risk(conn, symbol, direction):
return False, risk_reason return False, risk_reason
if not trading_day_reset_allows_new_open(now): if not trading_day_reset_allows_new_open(now):
return False, f"北京时间 {TRADING_DAY_RESET_HOUR}:00 前不允许持仓" return False, f"北京时间 {TRADING_DAY_RESET_HOUR}:00 前不允许持仓"
from account_risk_lib import position_limit_reached from lib.trade.account_risk_lib import position_limit_reached
reached, active_count, mx = position_limit_reached(conn, max_active_positions=MAX_ACTIVE_POSITIONS) reached, active_count, mx = position_limit_reached(conn, max_active_positions=MAX_ACTIVE_POSITIONS)
if reached: if reached:
@@ -2973,7 +2974,7 @@ def parse_ccxt_position_metrics(position, order_leverage=None):
cs = float(get_contract_size(sym)) if sym else 1.0 cs = float(get_contract_size(sym)) if sym else 1.0
except Exception: except Exception:
cs = 1.0 cs = 1.0
from hub_position_metrics import enrich_ccxt_position_metrics_out from lib.hub.hub_position_metrics import enrich_ccxt_position_metrics_out
enrich_ccxt_position_metrics_out( enrich_ccxt_position_metrics_out(
p, out, contract_size=cs, funds_decimals=FUNDS_DECIMALS p, out, contract_size=cs, funds_decimals=FUNDS_DECIMALS
@@ -6252,14 +6253,14 @@ def background_task():
check_trigger_entry_key_monitors() check_trigger_entry_key_monitors()
_roll_cfg = app.extensions.get("strategy_roll_cfg") _roll_cfg = app.extensions.get("strategy_roll_cfg")
if _roll_cfg: if _roll_cfg:
from strategy_roll_monitor_lib import check_roll_monitors from lib.strategy.strategy_roll_monitor_lib import check_roll_monitors
check_roll_monitors(_roll_cfg) check_roll_monitors(_roll_cfg)
check_key_monitors() check_key_monitors()
check_order_monitors() check_order_monitors()
cfg = app.extensions.get("strategy_trend_cfg") cfg = app.extensions.get("strategy_trend_cfg")
if cfg: if cfg:
from strategy_trend_register import check_trend_pullback_plans from lib.strategy.strategy_trend_register import check_trend_pullback_plans
check_trend_pullback_plans(cfg) check_trend_pullback_plans(cfg)
except: except:
@@ -6348,7 +6349,7 @@ def render_main_page(page="trade", embed_mode=None):
conn = get_db() conn = get_db()
session_row = ensure_session(conn, trading_day) session_row = ensure_session(conn, trading_day)
local_current_capital = float(session_row["current_capital"]) local_current_capital = float(session_row["current_capital"])
from instance_embed_context_lib import ( from lib.instance.instance_embed_context_lib import (
embed_render_plan, embed_render_plan,
minimal_stats_bundle, minimal_stats_bundle,
trade_records_summary, trade_records_summary,
@@ -6420,7 +6421,7 @@ def render_main_page(page="trade", embed_mode=None):
records = [] records = []
total = miss_count = rate = occupied_miss_total = 0 total = miss_count = rate = occupied_miss_total = 0
active_count = len(order_list) active_count = len(order_list)
from strategy_trade_labels import count_position_limit_active_monitors from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
position_limit_count = count_position_limit_active_monitors(conn) position_limit_count = count_position_limit_active_monitors(conn)
open_guard_enabled = get_trading_day_reset_open_guard_enabled(conn) open_guard_enabled = get_trading_day_reset_open_guard_enabled(conn)
@@ -6453,7 +6454,7 @@ def render_main_page(page="trade", embed_mode=None):
) )
strategy_extra = {} strategy_extra = {}
if plan.strategy: if plan.strategy:
from strategy_ui import strategy_render_extras from lib.strategy.strategy_ui import strategy_render_extras
strategy_extra = strategy_render_extras( strategy_extra = strategy_render_extras(
conn, conn,
@@ -6463,7 +6464,7 @@ def render_main_page(page="trade", embed_mode=None):
trend_cfg=app.extensions.get("strategy_trend_cfg"), trend_cfg=app.extensions.get("strategy_trend_cfg"),
) )
conn.close() conn.close()
from instance_embed_lib import embed_context_extras from lib.instance.instance_embed_lib import embed_context_extras
template_ctx = dict( template_ctx = dict(
page=page, page=page,
@@ -6600,7 +6601,7 @@ def api_account_snapshot():
funding_usdt = round(funding_capital, FUNDS_DECIMALS) if funding_capital is not None else None funding_usdt = round(funding_capital, FUNDS_DECIMALS) if funding_capital is not None else None
current_capital = round(trading_capital, FUNDS_DECIMALS) if trading_capital is not None else round(local_current_capital, FUNDS_DECIMALS) current_capital = round(trading_capital, FUNDS_DECIMALS) if trading_capital is not None else round(local_current_capital, FUNDS_DECIMALS)
recommended_capital = get_recommended_capital(current_capital) recommended_capital = get_recommended_capital(current_capital)
from strategy_trade_labels import count_position_limit_active_monitors from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
position_limit_count = count_position_limit_active_monitors(conn) position_limit_count = count_position_limit_active_monitors(conn)
open_guard_enabled = get_trading_day_reset_open_guard_enabled(conn) open_guard_enabled = get_trading_day_reset_open_guard_enabled(conn)
@@ -6651,7 +6652,7 @@ def api_settings_open_guard():
now = app_now() now = app_now()
conn = get_db() conn = get_db()
trading_day = get_trading_day(now) trading_day = get_trading_day(now)
from strategy_trade_labels import count_position_limit_active_monitors from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
position_limit_count = count_position_limit_active_monitors(conn) position_limit_count = count_position_limit_active_monitors(conn)
guard_on = get_trading_day_reset_open_guard_enabled(conn) guard_on = get_trading_day_reset_open_guard_enabled(conn)
@@ -6954,7 +6955,7 @@ def api_price_snapshot():
pass pass
conn.close() conn.close()
from hub_position_metrics import build_position_marks_list from lib.hub.hub_position_metrics import build_position_marks_list
position_marks = build_position_marks_list( position_marks = build_position_marks_list(
all_swap_positions, all_swap_positions,
@@ -7108,7 +7109,7 @@ def api_order_kline():
"volume": float(bar[5]), "volume": float(bar[5]),
}) })
from focus_chart_lib import ( from lib.instance.focus_chart_lib import (
build_order_kline_order_payload, build_order_kline_order_payload,
load_swap_positions_for_order_kline, load_swap_positions_for_order_kline,
metrics_for_order_item, metrics_for_order_item,
@@ -7136,7 +7137,7 @@ def api_order_kline():
ex_metrics=ex_metrics, ex_metrics=ex_metrics,
) )
from focus_chart_lib import kline_api_price_fields from lib.instance.focus_chart_lib import kline_api_price_fields
price_fields = kline_api_price_fields( price_fields = kline_api_price_fields(
exchange, exchange,
@@ -7252,7 +7253,7 @@ def api_key_kline():
"lower_pct": lower_pct, "lower_pct": lower_pct,
} }
from focus_chart_lib import enrich_key_kline_response from lib.instance.focus_chart_lib import enrich_key_kline_response
price_display, key_info = enrich_key_kline_response( price_display, key_info = enrich_key_kline_response(
symbol=symbol, symbol=symbol,
@@ -7261,7 +7262,7 @@ def api_key_kline():
format_price_fn=format_price_for_symbol, format_price_fn=format_price_for_symbol,
) )
from focus_chart_lib import kline_api_price_fields from lib.instance.focus_chart_lib import kline_api_price_fields
price_fields = kline_api_price_fields( price_fields = kline_api_price_fields(
exchange, exchange,
@@ -8241,7 +8242,7 @@ def del_order(id):
opened_at=opened_at, opened_at=opened_at,
closed_at=closed_at, closed_at=closed_at,
) )
from account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close from lib.trade.account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close
on_user_initiated_close( on_user_initiated_close(
conn, conn,
@@ -8255,7 +8256,7 @@ def del_order(id):
try: try:
_rcfg = app.extensions.get("strategy_roll_cfg") _rcfg = app.extensions.get("strategy_roll_cfg")
if isinstance(_rcfg, dict): if isinstance(_rcfg, dict):
from strategy_register import roll_sync_after_external_close from lib.strategy.strategy_register import roll_sync_after_external_close
roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"]) roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"])
except Exception: except Exception:
@@ -8314,7 +8315,7 @@ def del_order(id):
opened_at=opened_at, opened_at=opened_at,
closed_at=closed_at, closed_at=closed_at,
) )
from account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close from lib.trade.account_risk_lib import CLOSE_SOURCE_USER_INSTANCE, insert_trade_record_id, on_user_initiated_close
on_user_initiated_close( on_user_initiated_close(
conn, conn,
@@ -8328,7 +8329,7 @@ def del_order(id):
try: try:
_rcfg = app.extensions.get("strategy_roll_cfg") _rcfg = app.extensions.get("strategy_roll_cfg")
if isinstance(_rcfg, dict): if isinstance(_rcfg, dict):
from strategy_register import roll_sync_after_external_close from lib.strategy.strategy_register import roll_sync_after_external_close
roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"]) roll_sync_after_external_close(_rcfg, conn, row["symbol"], row["direction"])
except Exception: except Exception:
@@ -8489,7 +8490,7 @@ def add_journal():
d.get("post_breakeven_stare"), d.get("new_trade_while_occupied"), d.get("note"), image_filename d.get("post_breakeven_stare"), d.get("new_trade_while_occupied"), d.get("note"), image_filename
) )
) )
from account_risk_lib import on_journal_saved from lib.trade.account_risk_lib import on_journal_saved
on_journal_saved( on_journal_saved(
conn, conn,
@@ -8577,7 +8578,7 @@ def api_reviews():
return jsonify([row_to_dict(r) for r in rows]) return jsonify([row_to_dict(r) for r in rows])
_REPO_STATIC_DIR = os.path.join(os.path.dirname(BASE_DIR), "static") _REPO_STATIC_DIR = common_static_dir(os.path.dirname(BASE_DIR))
_AI_REVIEW_RENDER_JS = os.path.join(_REPO_STATIC_DIR, "ai_review_render.js") _AI_REVIEW_RENDER_JS = os.path.join(_REPO_STATIC_DIR, "ai_review_render.js")
_FORM_SUBMIT_GUARD_JS = os.path.join(_REPO_STATIC_DIR, "form_submit_guard.js") _FORM_SUBMIT_GUARD_JS = os.path.join(_REPO_STATIC_DIR, "form_submit_guard.js")
_MANUAL_ORDER_RR_PREVIEW_JS = os.path.join(_REPO_STATIC_DIR, "manual_order_rr_preview.js") _MANUAL_ORDER_RR_PREVIEW_JS = os.path.join(_REPO_STATIC_DIR, "manual_order_rr_preview.js")
@@ -8802,7 +8803,7 @@ def api_trade_record_review_update():
tuple(base_params + [rec_id]), tuple(base_params + [rec_id]),
) )
if reviewed_result == "手动平仓" and reviewed_miss_reason: if reviewed_result == "手动平仓" and reviewed_miss_reason:
from account_risk_lib import apply_manual_close_journal_cooloff from lib.trade.account_risk_lib import apply_manual_close_journal_cooloff
apply_manual_close_journal_cooloff( apply_manual_close_journal_cooloff(
conn, conn,
@@ -8952,7 +8953,7 @@ def _hub_account_bundle():
def _hub_fetch_market(base=""): def _hub_fetch_market(base=""):
from hub_market_info_lib import fetch_usdt_swap_market_info from lib.hub.hub_market_info_lib import fetch_usdt_swap_market_info
return fetch_usdt_swap_market_info( return fetch_usdt_swap_market_info(
base_or_symbol=base, base_or_symbol=base,
@@ -8965,7 +8966,7 @@ def _hub_fetch_market(base=""):
def _hub_fetch_ohlcv(symbol, timeframe, since_ms=None, limit=500): def _hub_fetch_ohlcv(symbol, timeframe, since_ms=None, limit=500):
from hub_ohlcv_lib import fetch_ohlcv_for_hub from lib.hub.hub_ohlcv_lib import fetch_ohlcv_for_hub
return fetch_ohlcv_for_hub( return fetch_ohlcv_for_hub(
symbol=symbol, symbol=symbol,
@@ -8981,7 +8982,7 @@ def _hub_fetch_ohlcv(symbol, timeframe, since_ms=None, limit=500):
def _hub_fetch_volume_rank(top_n=20): def _hub_fetch_volume_rank(top_n=20):
from hub_volume_rank_lib import fetch_usdt_swap_volume_rank from lib.hub.hub_volume_rank_lib import fetch_usdt_swap_volume_rank
return fetch_usdt_swap_volume_rank( return fetch_usdt_swap_volume_rank(
exchange=exchange, exchange=exchange,
@@ -8998,7 +8999,7 @@ try:
_repo_root = Path(__file__).resolve().parent.parent _repo_root = Path(__file__).resolve().parent.parent
if str(_repo_root) not in sys.path: if str(_repo_root) not in sys.path:
sys.path.insert(0, str(_repo_root)) sys.path.insert(0, str(_repo_root))
from hub_bridge import install_on_app from lib.hub.hub_bridge import install_on_app
install_on_app( install_on_app(
app, app,
@@ -9045,8 +9046,8 @@ def strategy_roll_page():
normalize_exchange_symbol = normalize_okx_symbol normalize_exchange_symbol = normalize_okx_symbol
ensure_exchange_live_ready = ensure_okx_live_ready ensure_exchange_live_ready = ensure_okx_live_ready
from strategy_register import install_strategy_trading from lib.strategy.strategy_register import install_strategy_trading
from strategy_trend_register import install_strategy_trend from lib.strategy.strategy_trend_register import install_strategy_trend
install_strategy_trading(app, _REPO_ROOT, app_module=sys.modules[__name__]) install_strategy_trading(app, _REPO_ROOT, app_module=sys.modules[__name__])
install_strategy_trend(app, _REPO_ROOT, app_module=sys.modules[__name__]) install_strategy_trend(app, _REPO_ROOT, app_module=sys.modules[__name__])
+147
View File
@@ -0,0 +1,147 @@
# lib/ 共用模块结构
四所实例与中控共用的 Python 库、模板与静态资源统一放在仓库根目录的 **`lib/`** 下。部署单元(`crypto_monitor_*``manual_trading_hub`)仍保持独立目录与 PM2 配置不变。
**重构前快照 Git 标签**`pre-lib-modularization`(可用 `git checkout pre-lib-modularization` 查看旧布局)。
---
## 顶层目录
```
crypto_monitor/
├── crypto_monitor_binance/ # 四所:各自 app + .env + PM2
├── crypto_monitor_gate/
├── crypto_monitor_gate_bot/
├── crypto_monitor_okx/
├── manual_trading_hub/ # 中控 + 子代理 agent
├── lib/ # 共用模块(本说明)
│ ├── strategy/
│ ├── key_monitor/
│ ├── trade/
│ ├── hub/
│ ├── ai/
│ ├── instance/
│ ├── exchange/
│ ├── common/
│ └── paths.py
├── brand/ # 各所共用图标
├── docs/
├── deploy/
├── scripts/
├── tests/
├── requirements.txt
└── README.md
```
---
## lib/ 子包说明
| 子包 | 职责 | 主要模块 |
|------|------|----------|
| **`lib/strategy/`** | 策略交易(顺势加仓、趋势回调、快照与记录) | `strategy_register.py``strategy_trend_register.py``strategy_db.py``strategy_roll_*``strategy_trend_*` |
| **`lib/strategy/templates/`** | 策略页 Jinja 模板(原 `strategy_templates/` | `strategy_trading_page.html``strategy_roll_panel.html` 等 |
| **`lib/key_monitor/`** | 关键位监控、斐波、假突破、止盈止损方案 | `key_monitor_lib.py``fib_key_monitor_lib.py``key_sl_tp_lib.py` 等 |
| **`lib/trade/`** | 下单监控展示、计仓、账户风控、手动 SL/TP | `order_monitor_display_lib.py``position_sizing_lib.py``account_risk_lib.py` 等 |
| **`lib/hub/`** | 中控 API、K 线、归档、计仓器、SSO/Bridge | `hub_bridge.py``hub_kline_store.py``hub_trades_lib.py` 等 |
| **`lib/ai/`** | AI 复盘与文本生成 | `ai_client.py``ai_review_lib.py` |
| **`lib/instance/`** | 中控 iframe 嵌入、导航、复盘图表 | `instance_embed_lib.py``focus_chart_lib.py``journal_chart_lib.py` |
| **`lib/instance/templates/`** | 嵌入页片段(原 `embed_templates/` | `embed_page_fragment.html` |
| **`lib/exchange/`** | 特定交易所工具 | `gate_transfer_lib.py``okx_orders_lib.py` 等 |
| **`lib/common/`** | 跨功能小工具 | `form_submit_lib.py``wechat_notify_lib.py` 等 |
| **`lib/common/static/`** | 四所与中控共用的 JS/CSS(原根目录 `static/` | `instance_theme.js``strategy_roll.js` 等 |
> **说明**:`hub_*` 命名表示「中控侧能力或行情聚合」,但部分模块(如 `hub_volume_rank_lib`、`hub_market_info_lib`)四所 `app.py` 也会调用,并非中控独占。
---
## 路径辅助函数
`lib/paths.py` 集中维护资源目录,避免硬编码:
```python
from lib.paths import strategy_templates_dir, embed_templates_dir, common_static_dir
strategy_templates_dir() # .../lib/strategy/templates
embed_templates_dir() # .../lib/instance/templates
common_static_dir() # .../lib/common/static
```
可选传入 `repo_root`(字符串或 `Path`),默认使用 `lib/` 的上级目录即仓库根。
---
## Python 导入约定
各部署目录在启动时将 **仓库根** 加入 `sys.path`(与重构前相同):
```python
_REPO_ROOT = os.path.dirname(BASE_DIR) # 或 Path(__file__).resolve().parent.parent
if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT)
```
之后使用 **`lib.<子包>.<模块>`** 形式导入,例如:
```python
from lib.strategy.strategy_db import init_strategy_tables
from lib.key_monitor.key_monitor_lib import check_key_monitors
from lib.hub.hub_bridge import install_on_app
from lib.ai.ai_client import ai_review
```
策略注册仍在各所 `app.py` 末尾:
```python
from lib.strategy.strategy_register import install_strategy_trading
from lib.strategy.strategy_trend_register import install_strategy_trend
install_strategy_trading(app, _REPO_ROOT, app_module=sys.modules[__name__])
install_strategy_trend(app, _REPO_ROOT, app_module=sys.modules[__name__])
```
---
## 静态资源与 URL
- 四所页面仍通过 **`/static/...`** 访问共用脚本;`hub_bridge.install_instance_theme_static``lib/common/static/` 提供部分根级静态路由。
- 各所目录下 **`static/`**(图标、上传图片等)仍为实例私有,未迁入 `lib/`
- 中控 `manual_trading_hub/hub.py` 通过 `_REPO_ROOT / "lib" / "common" / "static"` 挂载与四所共用的 badge、复盘 JS 等。
---
## 测试
在仓库根执行(需将根目录置于 Python 路径,或从根目录运行):
```bash
cd /opt/crypto_monitor
python -m unittest discover -s tests -p "test_*.py"
```
测试文件内统一 `from lib.<子包>.<模块> import ...`。使用 `@patch` 时目标写完整模块路径,例如 `lib.hub.hub_calculator_lib._resolve_market`
---
## 迁移脚本
一次性迁移由 `scripts/migrate_to_lib.py` 完成(移动文件 + 批量改写 import)。**不要在已迁移后的仓库上重复执行**。
---
## 后续可选整理
- 四所 `app.py` 体量接近,可逐步抽取公共 `exchange_app` 基座(改动面大,单独规划)。
- `manual_trading_hub/okx_orders_lib.py` 为 agent 本地副本,可与 `lib/exchange/okx_orders_lib.py` 合并去重。
- 可引入 `pyproject.toml` + `pip install -e .`,替代 `sys.path.insert`(长期维护更规范)。
---
## 相关文档
- [README.md](../README.md) — 总览与部署
- [策略交易说明.md](../策略交易说明.md)
- [manual_trading_hub/使用说明.md](../manual_trading_hub/使用说明.md)
+1
View File
@@ -0,0 +1 @@
"""crypto_monitor shared libraries."""
+1
View File
@@ -0,0 +1 @@
"""Shared library package."""
+1 -1
View File
@@ -5,7 +5,7 @@ import os
import uuid import uuid
from typing import Any, Callable, List, Mapping, Optional, Sequence from typing import Any, Callable, List, Mapping, Optional, Sequence
from journal_chart_lib import ( from lib.instance.journal_chart_lib import (
JOURNAL_CHART_ANCHOR_CLOSE, JOURNAL_CHART_ANCHOR_CLOSE,
JOURNAL_CHART_DEFAULT_LIMIT, JOURNAL_CHART_DEFAULT_LIMIT,
JOURNAL_CHART_DEFAULT_TF1, JOURNAL_CHART_DEFAULT_TF1,
+1
View File
@@ -0,0 +1 @@
"""Shared library package."""
+1
View File
@@ -0,0 +1 @@
"""Shared library package."""
+1
View File
@@ -0,0 +1 @@
"""Shared library package."""
+1 -1
View File
@@ -3,7 +3,7 @@ from __future__ import annotations
import os import os
from hub_sso import ( from lib.hub.hub_sso import (
HUB_SSO_TTL_SEC, HUB_SSO_TTL_SEC,
hub_bridge_token, hub_bridge_token,
mint_hub_sso_token, mint_hub_sso_token,
+15 -13
View File
@@ -19,8 +19,8 @@ from flask import (
session, session,
) )
from hub_auth import request_allowed from lib.hub.hub_auth import request_allowed
from hub_sso import ( from lib.hub.hub_sso import (
mint_hub_embed_bootstrap, mint_hub_embed_bootstrap,
safe_next_path, safe_next_path,
verify_hub_embed_bootstrap, verify_hub_embed_bootstrap,
@@ -42,12 +42,14 @@ def _merge_query_into_path(path: str, **params: str) -> str:
def install_instance_theme_static(app) -> None: def install_instance_theme_static(app) -> None:
"""仓库 static/instance_theme.* 供四所页面共用。""" """仓库 lib/common/staticinstance_theme.* 供四所页面共用。"""
import os import os
from flask import Response, send_file from flask import Response, send_file
repo_static = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static") from lib.paths import common_static_dir
repo_static = common_static_dir()
assets = { assets = {
"instance_theme.js": "application/javascript; charset=utf-8", "instance_theme.js": "application/javascript; charset=utf-8",
"instance_theme_early.css": "text/css; charset=utf-8", "instance_theme_early.css": "text/css; charset=utf-8",
@@ -97,7 +99,7 @@ def register_trade_stats_calendar_route(
"""四所统计分析页:按月返回各交易日盈亏/笔数。""" """四所统计分析页:按月返回各交易日盈亏/笔数。"""
from flask import jsonify, request from flask import jsonify, request
from trade_stats_calendar_lib import build_trade_stats_calendar from lib.trade.trade_stats_calendar_lib import build_trade_stats_calendar
@app.route("/api/stats/calendar") @app.route("/api/stats/calendar")
@login_required_fn @login_required_fn
@@ -261,7 +263,7 @@ def _embed_login_dest(next_path: str) -> str:
ht = (request.args.get("hub_theme") or "").strip().lower() ht = (request.args.get("hub_theme") or "").strip().lower()
hub_theme = ht if ht in ("light", "dark") else None hub_theme = ht if ht in ("light", "dark") else None
if request.args.get("embed", "").strip().lower() in ("1", "true", "yes", "on"): if request.args.get("embed", "").strip().lower() in ("1", "true", "yes", "on"):
from instance_embed_lib import rewrite_embed_dest from lib.instance.instance_embed_lib import rewrite_embed_dest
return rewrite_embed_dest(next_path, hub_theme=hub_theme) return rewrite_embed_dest(next_path, hub_theme=hub_theme)
if hub_theme: if hub_theme:
@@ -312,7 +314,7 @@ def install_on_app(
if render_main_page_fn and login_required_fn: if render_main_page_fn and login_required_fn:
import os import os
from instance_embed_lib import attach_embed_templates, register_embed_routes from lib.instance.instance_embed_lib import attach_embed_templates, register_embed_routes
repo_root = os.path.dirname(os.path.abspath(__file__)) repo_root = os.path.dirname(os.path.abspath(__file__))
attach_embed_templates(app, repo_root) attach_embed_templates(app, repo_root)
@@ -519,7 +521,7 @@ def register_hub_routes(app):
).fetchall(): ).fetchall():
od = _row_to_dict(row) od = _row_to_dict(row)
try: try:
from strategy_trade_labels import apply_order_monitor_source_labels from lib.strategy.strategy_trade_labels import apply_order_monitor_source_labels
od = apply_order_monitor_source_labels(od) od = apply_order_monitor_source_labels(od)
except Exception: except Exception:
@@ -578,7 +580,7 @@ def register_hub_routes(app):
@_hub_auth_required @_hub_auth_required
def api_hub_trades_archive(): def api_hub_trades_archive():
"""中控币种档案:近 N 天已平仓记录。""" """中控币种档案:近 N 天已平仓记录。"""
from hub_trades_lib import fetch_trades_for_archive, summarize_trades from lib.hub.hub_trades_lib import fetch_trades_for_archive, summarize_trades
c = _ctx() c = _ctx()
get_db = c.get("get_db") get_db = c.get("get_db")
@@ -625,7 +627,7 @@ def register_hub_routes(app):
@_hub_auth_required @_hub_auth_required
def api_hub_trades_today(): def api_hub_trades_today():
"""中控 AI:当日已平仓记录(按实例交易日)。""" """中控 AI:当日已平仓记录(按实例交易日)。"""
from hub_trades_lib import ( from lib.hub.hub_trades_lib import (
current_trading_day, current_trading_day,
fetch_trades_for_trading_day, fetch_trades_for_trading_day,
summarize_trades, summarize_trades,
@@ -833,7 +835,7 @@ def register_hub_routes(app):
get_db = _ctx().get("get_db") get_db = _ctx().get("get_db")
if not cfg or not callable(get_db): if not cfg or not callable(get_db):
return jsonify({"ok": False, "msg": "趋势配置未就绪"}), 500 return jsonify({"ok": False, "msg": "趋势配置未就绪"}), 500
from strategy_trend_register import sync_trend_plans_after_external_close from lib.strategy.strategy_trend_register import sync_trend_plans_after_external_close
conn = get_db() conn = get_db()
try: try:
@@ -863,7 +865,7 @@ def register_hub_routes(app):
get_db = _ctx().get("get_db") get_db = _ctx().get("get_db")
if not cfg or not callable(get_db): if not cfg or not callable(get_db):
return jsonify({"ok": False, "msg": "滚仓配置未就绪"}), 500 return jsonify({"ok": False, "msg": "滚仓配置未就绪"}), 500
from strategy_register import roll_sync_after_external_close from lib.strategy.strategy_register import roll_sync_after_external_close
conn = get_db() conn = get_db()
try: try:
@@ -987,7 +989,7 @@ def _fetch_preview(pid):
now_ms = int(time.time() * 1000) now_ms = int(time.time() * 1000)
d["expires_in_sec"] = max(0, int((int(d.get("expires_at_ms") or 0) - now_ms) / 1000)) d["expires_in_sec"] = max(0, int((int(d.get("expires_at_ms") or 0) - now_ms) / 1000))
try: try:
from strategy_trend_lib import build_trend_preview_level_rows from lib.strategy.strategy_trend_lib import build_trend_preview_level_rows
enriched, level_rows = build_trend_preview_level_rows(d) enriched, level_rows = build_trend_preview_level_rows(d)
for key in ( for key in (
@@ -3,8 +3,8 @@ from __future__ import annotations
from typing import Any, Callable, Optional, Tuple from typing import Any, Callable, Optional, Tuple
from strategy_roll_lib import max_roll_legs from lib.strategy.strategy_roll_lib import max_roll_legs
from strategy_trend_lib import ( from lib.strategy.strategy_trend_lib import (
build_trend_preview_level_rows, build_trend_preview_level_rows,
calc_risk_fraction, calc_risk_fraction,
compute_trend_plan_core, compute_trend_plan_core,
@@ -19,7 +19,7 @@ def _resolve_market(
exchange_id: str, exchange_id: str,
base: str, base: str,
) -> Tuple[Optional[dict[str, Any]], Optional[Callable[[float], Optional[float]]], Optional[str]]: ) -> Tuple[Optional[dict[str, Any]], Optional[Callable[[float], Optional[float]]], Optional[str]]:
from hub_calculator_market_lib import get_calculator_market, make_amount_precise_fn_from_market from lib.hub.hub_calculator_market_lib import get_calculator_market, make_amount_precise_fn_from_market
market, err = get_calculator_market(exchange_id, base) market, err = get_calculator_market(exchange_id, base)
if err or not market: if err or not market:
@@ -99,7 +99,7 @@ def amount_decimals_from_exchange(exchange: Any, exchange_symbol: str) -> int:
def price_decimals_from_exchange( def price_decimals_from_exchange(
exchange: Any, exchange_symbol: str, price_tick: Optional[float] exchange: Any, exchange_symbol: str, price_tick: Optional[float]
) -> int: ) -> int:
from hub_ohlcv_lib import normalize_price_tick from lib.hub.hub_ohlcv_lib import normalize_price_tick
tick = normalize_price_tick(price_tick) tick = normalize_price_tick(price_tick)
if tick and tick > 0: if tick and tick > 0:
@@ -7,7 +7,7 @@ from datetime import datetime, timedelta
from pathlib import Path from pathlib import Path
from typing import Any, Optional from typing import Any, Optional
from hub_trades_lib import current_trading_day from lib.hub.hub_trades_lib import current_trading_day
HUB_DIR = Path(__file__).resolve().parent / "manual_trading_hub" HUB_DIR = Path(__file__).resolve().parent / "manual_trading_hub"
FUND_HISTORY_PATH = HUB_DIR / "hub_fund_history.json" FUND_HISTORY_PATH = HUB_DIR / "hub_fund_history.json"
@@ -8,7 +8,7 @@ import time
from pathlib import Path from pathlib import Path
from typing import Any, Callable, Optional from typing import Any, Callable, Optional
from hub_ohlcv_lib import ( from lib.hub.hub_ohlcv_lib import (
HUB_KLINE_1M_MAX_BARS, HUB_KLINE_1M_MAX_BARS,
HUB_KLINE_5M_1H_RETENTION_DAYS, HUB_KLINE_5M_1H_RETENTION_DAYS,
TIMEFRAME_MS, TIMEFRAME_MS,
@@ -10,7 +10,7 @@ from pathlib import Path
from typing import Any from typing import Any
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
from hub_symbol_archive_lib import parse_wall_clock_ms from lib.hub.hub_symbol_archive_lib import parse_wall_clock_ms
DISPLAY_TZ = ZoneInfo(os.getenv("APP_TIMEZONE", "Asia/Shanghai")) DISPLAY_TZ = ZoneInfo(os.getenv("APP_TIMEZONE", "Asia/Shanghai"))
@@ -4,13 +4,13 @@ from __future__ import annotations
from typing import Any, Callable, Optional, Tuple from typing import Any, Callable, Optional, Tuple
from hub_calculator_market_lib import ( from lib.hub.hub_calculator_market_lib import (
amount_decimals_from_exchange, amount_decimals_from_exchange,
normalize_base_symbol, normalize_base_symbol,
price_decimals_from_exchange, price_decimals_from_exchange,
resolve_usdt_perp_symbol, resolve_usdt_perp_symbol,
) )
from hub_ohlcv_lib import normalize_price_tick, price_tick_from_market from lib.hub.hub_ohlcv_lib import normalize_price_tick, price_tick_from_market
def fetch_usdt_swap_market_info( def fetch_usdt_swap_market_info(
View File
@@ -13,13 +13,13 @@ from zoneinfo import ZoneInfo
CHART_DISPLAY_TZ = ZoneInfo(os.getenv("APP_TIMEZONE", "Asia/Shanghai")) CHART_DISPLAY_TZ = ZoneInfo(os.getenv("APP_TIMEZONE", "Asia/Shanghai"))
from hub_ohlcv_lib import ( from lib.hub.hub_ohlcv_lib import (
TIMEFRAME_MS, TIMEFRAME_MS,
aggregate_ohlcv_bars, aggregate_ohlcv_bars,
normalize_chart_timeframe, normalize_chart_timeframe,
normalize_perpetual_symbol, normalize_perpetual_symbol,
) )
from hub_trades_lib import ( from lib.hub.hub_trades_lib import (
display_entry_type_label, display_entry_type_label,
effective_hold_minutes, effective_hold_minutes,
format_hold_minutes, format_hold_minutes,
@@ -4,12 +4,12 @@ from __future__ import annotations
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Any, Callable, Optional from typing import Any, Callable, Optional
from strategy_trade_labels import ( from lib.strategy.strategy_trade_labels import (
MONITOR_TYPE_ROLL, MONITOR_TYPE_ROLL,
MONITOR_TYPE_TREND_PULLBACK, MONITOR_TYPE_TREND_PULLBACK,
entry_reason_for_monitor_type, entry_reason_for_monitor_type,
) )
from time_close_lib import TIME_CLOSE_RESULT from lib.trade.time_close_lib import TIME_CLOSE_RESULT
TRADE_COMPLETED_RESULTS = ( TRADE_COMPLETED_RESULTS = (
"止盈", "止盈",
@@ -9,7 +9,7 @@ from pathlib import Path
from typing import Any, Callable from typing import Any, Callable
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
from hub_trades_lib import trading_day_from_dt from lib.hub.hub_trades_lib import trading_day_from_dt
TOP_N_DEFAULT = 20 TOP_N_DEFAULT = 20
CACHE_VERSION = 3 CACHE_VERSION = 3
+1
View File
@@ -0,0 +1 @@
"""Shared library package."""
@@ -3,12 +3,12 @@ from __future__ import annotations
from typing import Any, Callable, Optional from typing import Any, Callable, Optional
from hub_ohlcv_lib import ( from lib.hub.hub_ohlcv_lib import (
normalize_price_tick, normalize_price_tick,
price_tick_from_market, price_tick_from_market,
round_ohlcv_bars_to_tick, round_ohlcv_bars_to_tick,
) )
from order_monitor_display_lib import ( from lib.trade.order_monitor_display_lib import (
apply_order_live_price_display, apply_order_live_price_display,
apply_order_price_display_fields, apply_order_price_display_fields,
) )
@@ -51,7 +51,7 @@ def embed_render_plan(page: str, embed_mode: str | None) -> EmbedRenderPlan:
def trade_records_summary(conn, start_bj: str, end_bj: str, tr_ts: str) -> dict[str, Any]: def trade_records_summary(conn, start_bj: str, end_bj: str, tr_ts: str) -> dict[str, Any]:
"""顶栏统计用 COUNT,避免 embed 壳拉 1000 行交易记录。""" """顶栏统计用 COUNT,避免 embed 壳拉 1000 行交易记录。"""
from trade_result_lib import sql_effective_pnl_expr from lib.trade.trade_result_lib import sql_effective_pnl_expr
pnl_sql = sql_effective_pnl_expr() pnl_sql = sql_effective_pnl_expr()
row = conn.execute( row = conn.execute(
@@ -1,7 +1,8 @@
"""中控 iframe:壳常驻 + tab 内容 API/embed、/api/embed/page/<tab>)。""" """中控 iframe:壳常驻 + tab 内容 API/embed、/api/embed/page/<tab>)。"""
from __future__ import annotations from __future__ import annotations
from lib.paths import embed_templates_dir
import os import os
from typing import Callable from typing import Callable
from urllib.parse import parse_qsl, urlencode, urlsplit from urllib.parse import parse_qsl, urlencode, urlsplit
@@ -99,7 +100,7 @@ def rewrite_embed_dest(path: str, hub_theme: str | None = None) -> str:
def attach_embed_templates(app: Flask, repo_root: str) -> None: def attach_embed_templates(app: Flask, repo_root: str) -> None:
embed_dir = os.path.join(repo_root, "embed_templates") embed_dir = embed_templates_dir(repo_root)
if not os.path.isdir(embed_dir): if not os.path.isdir(embed_dir):
return return
existing = app.jinja_loader existing = app.jinja_loader
+1
View File
@@ -0,0 +1 @@
"""Shared library package."""
@@ -17,7 +17,7 @@ def is_false_breakout_key_monitor_type(monitor_type: Optional[str]) -> bool:
def is_limit_key_monitor_type(monitor_type: Optional[str]) -> bool: def is_limit_key_monitor_type(monitor_type: Optional[str]) -> bool:
from fib_key_monitor_lib import is_fib_key_monitor_type from lib.key_monitor.fib_key_monitor_lib import is_fib_key_monitor_type
return is_fib_key_monitor_type(monitor_type) or is_false_breakout_key_monitor_type(monitor_type) return is_fib_key_monitor_type(monitor_type) or is_false_breakout_key_monitor_type(monitor_type)
@@ -1,6 +1,6 @@
"""斐波关键位监控:纯计算与类型判断(Gate / Binance 主站共用)。""" """斐波关键位监控:纯计算与类型判断(Gate / Binance 主站共用)。"""
from key_monitor_lib import KEY_MONITOR_AUTO_TYPES from lib.key_monitor.key_monitor_lib import KEY_MONITOR_AUTO_TYPES
FIB_KEY_MONITOR_TYPES = frozenset({"斐波回调0.618", "斐波回调0.786"}) FIB_KEY_MONITOR_TYPES = frozenset({"斐波回调0.618", "斐波回调0.786"})
KEY_MONITOR_TRADE_TYPE = "关键位监控" KEY_MONITOR_TRADE_TYPE = "关键位监控"
@@ -5,10 +5,10 @@ from __future__ import annotations
from typing import Any, Callable, Iterable, Optional from typing import Any, Callable, Iterable, Optional
from fib_key_monitor_lib import FIB_KEY_MONITOR_TYPES, is_fib_key_monitor_type from lib.key_monitor.fib_key_monitor_lib import FIB_KEY_MONITOR_TYPES, is_fib_key_monitor_type
from false_breakout_key_monitor_lib import is_false_breakout_key_monitor_type from lib.key_monitor.false_breakout_key_monitor_lib import is_false_breakout_key_monitor_type
from key_monitor_lib import KEY_MONITOR_AUTO_TYPES from lib.key_monitor.key_monitor_lib import KEY_MONITOR_AUTO_TYPES
from position_sizing_lib import is_full_margin_mode, mode_label_zh from lib.trade.position_sizing_lib import is_full_margin_mode, mode_label_zh
def monitor_type_disallowed_in_full_margin(monitor_type: str) -> bool: def monitor_type_disallowed_in_full_margin(monitor_type: str) -> bool:
@@ -356,12 +356,12 @@ def key_monitor_rule_template_context(
trigger_entry_validity_hours: int | None = None, trigger_entry_validity_hours: int | None = None,
) -> dict[str, Any]: ) -> dict[str, Any]:
"""关键位监控页规则说明表格(Jinja key_rule_ctx)。""" """关键位监控页规则说明表格(Jinja key_rule_ctx)。"""
from false_breakout_key_monitor_lib import ( from lib.key_monitor.false_breakout_key_monitor_lib import (
FALSE_BREAKOUT_OFFSET_PCT, FALSE_BREAKOUT_OFFSET_PCT,
FALSE_BREAKOUT_RR, FALSE_BREAKOUT_RR,
FALSE_BREAKOUT_SL_PCT, FALSE_BREAKOUT_SL_PCT,
) )
from trigger_entry_key_monitor_lib import TRIGGER_ENTRY_VALIDITY_HOURS from lib.key_monitor.trigger_entry_key_monitor_lib import TRIGGER_ENTRY_VALIDITY_HOURS
te_hours = ( te_hours = (
int(trigger_entry_validity_hours) int(trigger_entry_validity_hours)
@@ -4,12 +4,12 @@ from __future__ import annotations
from datetime import datetime from datetime import datetime
from typing import Any, Callable, Optional from typing import Any, Callable, Optional
from false_breakout_key_monitor_lib import ( from lib.key_monitor.false_breakout_key_monitor_lib import (
_parse_created_at, _parse_created_at,
expires_at_text, expires_at_text,
is_false_breakout_expired, is_false_breakout_expired,
) )
from strategy_trend_lib import trend_dca_level_reached from lib.strategy.strategy_trend_lib import trend_dca_level_reached
# 回调触价(原「触价开仓」) # 回调触价(原「触价开仓」)
CALLBACK_TRIGGER_ENTRY_MONITOR_TYPE = "回调触价开仓" CALLBACK_TRIGGER_ENTRY_MONITOR_TYPE = "回调触价开仓"
+22
View File
@@ -0,0 +1,22 @@
"""Repository path helpers for lib/ assets."""
from __future__ import annotations
from pathlib import Path
LIB_DIR = Path(__file__).resolve().parent
REPO_ROOT = LIB_DIR.parent
def strategy_templates_dir(repo_root: str | Path | None = None) -> str:
root = Path(repo_root) if repo_root is not None else REPO_ROOT
return str(root / "lib" / "strategy" / "templates")
def embed_templates_dir(repo_root: str | Path | None = None) -> str:
root = Path(repo_root) if repo_root is not None else REPO_ROOT
return str(root / "lib" / "instance" / "templates")
def common_static_dir(repo_root: str | Path | None = None) -> str:
root = Path(repo_root) if repo_root is not None else REPO_ROOT
return str(root / "lib" / "common" / "static")
+1
View File
@@ -0,0 +1 @@
"""Shared library package."""
@@ -133,7 +133,7 @@ CREATE TABLE IF NOT EXISTS trend_pullback_preview_snapshots (
def init_strategy_tables(conn) -> None: def init_strategy_tables(conn) -> None:
from strategy_snapshot_lib import init_strategy_snapshot_table from lib.strategy.strategy_snapshot_lib import init_strategy_snapshot_table
conn.execute(ROLL_GROUPS_SQL) conn.execute(ROLL_GROUPS_SQL)
conn.execute(ROLL_LEGS_SQL) conn.execute(ROLL_LEGS_SQL)
@@ -1,4 +1,4 @@
"""Binance USDT-M 永续 — 策略交易交易所适配(见 strategy_config.build_strategy_config)。""" """Binance USDT-M 永续 — 策略交易交易所适配(见 strategy_config.build_strategy_config)。"""
from strategy_exchange_base import StrategyExchangeAdapter from lib.strategy.strategy_exchange_base import StrategyExchangeAdapter
__all__ = ["StrategyExchangeAdapter"] __all__ = ["StrategyExchangeAdapter"]
@@ -4,6 +4,6 @@ Gate.io USDT 永续 — 策略交易交易所侧能力。
实现方式 Gate 实例 app 通过 strategy_config.build_strategy_config(app_module) 注入 实现方式 Gate 实例 app 通过 strategy_config.build_strategy_config(app_module) 注入
ccxt 下单精度 TP/SL本文件为文档与类型锚点避免在四个 app 重复实现滚仓公式 ccxt 下单精度 TP/SL本文件为文档与类型锚点避免在四个 app 重复实现滚仓公式
""" """
from strategy_exchange_base import StrategyExchangeAdapter from lib.strategy.strategy_exchange_base import StrategyExchangeAdapter
__all__ = ["StrategyExchangeAdapter"] __all__ = ["StrategyExchangeAdapter"]
@@ -1,4 +1,4 @@
"""OKX 永续 — 策略交易交易所适配(见 strategy_config.build_strategy_config)。""" """OKX 永续 — 策略交易交易所适配(见 strategy_config.build_strategy_config)。"""
from strategy_exchange_base import StrategyExchangeAdapter from lib.strategy.strategy_exchange_base import StrategyExchangeAdapter
__all__ = ["StrategyExchangeAdapter"] __all__ = ["StrategyExchangeAdapter"]
@@ -6,7 +6,7 @@ from typing import Any
from flask import flash, redirect, url_for from flask import flash, redirect, url_for
from strategy_snapshot_lib import ( from lib.strategy.strategy_snapshot_lib import (
STRATEGY_SNAPSHOTS_MAX_ROWS, STRATEGY_SNAPSHOTS_MAX_ROWS,
dedupe_strategy_snapshots, dedupe_strategy_snapshots,
list_strategy_snapshots_split, list_strategy_snapshots_split,
@@ -1,6 +1,8 @@
"""策略交易:Flask 路由注册(顺势加仓 + 趋势回调页)。逻辑在 strategy_*_lib。""" """策略交易:Flask 路由注册(顺势加仓 + 趋势回调页)。逻辑在 strategy_*_lib。"""
from __future__ import annotations from __future__ import annotations
from lib.paths import strategy_templates_dir
import html as html_module import html as html_module
import os import os
import re import re
@@ -9,9 +11,9 @@ from typing import Any, Optional
from flask import Flask, flash, jsonify, redirect, render_template, request, url_for from flask import Flask, flash, jsonify, redirect, render_template, request, url_for
from jinja2 import ChoiceLoader, FileSystemLoader from jinja2 import ChoiceLoader, FileSystemLoader
from strategy_db import init_strategy_tables from lib.strategy.strategy_db import init_strategy_tables
from strategy_roll_lib import BREAKOUT_MODE, FIB_MODES, MARKET_MODE, preview_roll from lib.strategy.strategy_roll_lib import BREAKOUT_MODE, FIB_MODES, MARKET_MODE, preview_roll
from strategy_roll_monitor_lib import ( from lib.strategy.strategy_roll_monitor_lib import (
cancel_roll_pending_leg, cancel_roll_pending_leg,
count_filled_roll_legs, count_filled_roll_legs,
count_pending_roll_legs, count_pending_roll_legs,
@@ -25,7 +27,7 @@ def _dedupe_strategy_snapshots_on_startup(cfg: dict[str, Any]) -> None:
if not callable(get_db): if not callable(get_db):
return return
try: try:
from strategy_snapshot_lib import dedupe_strategy_snapshots from lib.strategy.strategy_snapshot_lib import dedupe_strategy_snapshots
conn = get_db() conn = get_db()
try: try:
@@ -44,13 +46,13 @@ def _dedupe_strategy_snapshots_on_startup(cfg: dict[str, Any]) -> None:
def install_strategy_trading(app: Flask, repo_root: str, app_module: Any = None, **build_kw) -> None: def install_strategy_trading(app: Flask, repo_root: str, app_module: Any = None, **build_kw) -> None:
"""在 app.py 末尾调用(login_required 已定义后)。仅注册 POST API;页面由各 app 的 render_main_page 渲染。""" """在 app.py 末尾调用(login_required 已定义后)。仅注册 POST API;页面由各 app 的 render_main_page 渲染。"""
from strategy_config import build_strategy_config from lib.strategy.strategy_config import build_strategy_config
build_kw.pop("render_trend_page", None) build_kw.pop("render_trend_page", None)
attach_strategy_templates(app, repo_root) attach_strategy_templates(app, repo_root)
cfg = build_strategy_config(app_module, **build_kw) cfg = build_strategy_config(app_module, **build_kw)
register_strategy_trading(app, cfg) register_strategy_trading(app, cfg)
from strategy_records_register import register_strategy_records from lib.strategy.strategy_records_register import register_strategy_records
register_strategy_records(app, cfg) register_strategy_records(app, cfg)
app.extensions["strategy_roll_cfg"] = cfg app.extensions["strategy_roll_cfg"] = cfg
@@ -58,7 +60,7 @@ def install_strategy_trading(app: Flask, repo_root: str, app_module: Any = None,
def attach_strategy_templates(app: Flask, repo_root: str) -> None: def attach_strategy_templates(app: Flask, repo_root: str) -> None:
strat_dir = os.path.join(repo_root, "strategy_templates") strat_dir = strategy_templates_dir(repo_root)
if not os.path.isdir(strat_dir): if not os.path.isdir(strat_dir):
return return
existing = app.jinja_loader existing = app.jinja_loader
@@ -262,7 +264,7 @@ def _roll_context(cfg: dict, data: dict) -> tuple[Optional[dict], Optional[str]]
m = cfg.get("app_module") m = cfg.get("app_module")
if m is not None: if m is not None:
try: try:
from position_sizing_lib import OPEN_SOURCE_ROLL, assert_open_source_allowed from lib.trade.position_sizing_lib import OPEN_SOURCE_ROLL, assert_open_source_allowed
mode = getattr(m, "POSITION_SIZING_MODE", None) or "risk" mode = getattr(m, "POSITION_SIZING_MODE", None) or "risk"
ok_src, src_msg = assert_open_source_allowed(mode, OPEN_SOURCE_ROLL) ok_src, src_msg = assert_open_source_allowed(mode, OPEN_SOURCE_ROLL)
@@ -538,7 +540,7 @@ def _maybe_notify_roll_started(cfg, rg, mon, symbol, direction, tp0, new_sl, *,
if not roll_is_new: if not roll_is_new:
return return
try: try:
from strategy_wechat_notify import notify_roll_group_started from lib.strategy.strategy_wechat_notify import notify_roll_group_started
notify_roll_group_started( notify_roll_group_started(
cfg, cfg,
@@ -3,7 +3,7 @@ from __future__ import annotations
from typing import Any, Optional, Tuple from typing import Any, Optional, Tuple
from fib_key_monitor_lib import calc_fib_plan, fib_invalidate_by_mark from lib.key_monitor.fib_key_monitor_lib import calc_fib_plan, fib_invalidate_by_mark
ROLL_MAX_LEGS_LONG = 3 ROLL_MAX_LEGS_LONG = 3
ROLL_MAX_LEGS_SHORT = 3 ROLL_MAX_LEGS_SHORT = 3
@@ -3,7 +3,7 @@ from __future__ import annotations
from typing import Any, Optional from typing import Any, Optional
from strategy_roll_lib import ( from lib.strategy.strategy_roll_lib import (
BREAKOUT_MODE, BREAKOUT_MODE,
FIB_MODES, FIB_MODES,
MARKET_MODE, MARKET_MODE,
@@ -17,7 +17,7 @@ from strategy_roll_lib import (
preview_roll, preview_roll,
solve_add_amount_for_total_risk, solve_add_amount_for_total_risk,
) )
from strategy_db import init_strategy_tables from lib.strategy.strategy_db import init_strategy_tables
ROLL_LEG_STATUS_LABELS = { ROLL_LEG_STATUS_LABELS = {
"pending": "监控中", "pending": "监控中",
@@ -78,7 +78,7 @@ def sync_roll_after_external_close(
if getattr(cur, "rowcount", 0): if getattr(cur, "rowcount", 0):
closed += 1 closed += 1
try: try:
from strategy_wechat_notify import notify_roll_group_ended from lib.strategy.strategy_wechat_notify import notify_roll_group_ended
notify_roll_group_ended( notify_roll_group_ended(
cfg, cfg,
@@ -91,7 +91,7 @@ def sync_roll_after_external_close(
except Exception: except Exception:
pass pass
try: try:
from strategy_snapshot_lib import save_roll_group_snapshot from lib.strategy.strategy_snapshot_lib import save_roll_group_snapshot
save_roll_group_snapshot(cfg, conn, g, result_label="结束") save_roll_group_snapshot(cfg, conn, g, result_label="结束")
except Exception: except Exception:
@@ -183,7 +183,7 @@ def _close_roll_group(conn, cfg: dict, group: dict, *, reason: str = "下单监
) )
if getattr(cur, "rowcount", 0): if getattr(cur, "rowcount", 0):
try: try:
from strategy_wechat_notify import notify_roll_group_ended from lib.strategy.strategy_wechat_notify import notify_roll_group_ended
notify_roll_group_ended( notify_roll_group_ended(
cfg, cfg,
@@ -196,7 +196,7 @@ def _close_roll_group(conn, cfg: dict, group: dict, *, reason: str = "下单监
except Exception: except Exception:
pass pass
try: try:
from strategy_snapshot_lib import save_roll_group_snapshot from lib.strategy.strategy_snapshot_lib import save_roll_group_snapshot
save_roll_group_snapshot(cfg, conn, group, result_label="结束") save_roll_group_snapshot(cfg, conn, group, result_label="结束")
except Exception: except Exception:
@@ -118,7 +118,7 @@ def build_trend_dca_levels(plan: dict) -> list[dict]:
def attach_trend_dca_levels(plan: dict) -> dict: def attach_trend_dca_levels(plan: dict) -> dict:
from strategy_trend_lib import enrich_trend_dca_levels_with_tp from lib.strategy.strategy_trend_lib import enrich_trend_dca_levels_with_tp
d = dict(plan or {}) d = dict(plan or {})
levels = build_trend_dca_levels(d) levels = build_trend_dca_levels(d)
@@ -305,7 +305,7 @@ def save_roll_group_snapshot(
).fetchall(): ).fetchall():
ld = _row_dict(leg) ld = _row_dict(leg)
try: try:
from strategy_roll_monitor_lib import roll_leg_status_label from lib.strategy.strategy_roll_monitor_lib import roll_leg_status_label
ld["status_label"] = roll_leg_status_label(ld.get("status")) ld["status_label"] = roll_leg_status_label(ld.get("status"))
except Exception: except Exception:
@@ -308,7 +308,7 @@ def calc_tp_profit_usdt(
) -> Optional[float]: ) -> Optional[float]:
"""到达止盈价时,按累计张数与加仓后均价的盈利 U。""" """到达止盈价时,按累计张数与加仓后均价的盈利 U。"""
try: try:
from hub_position_metrics import estimate_linear_swap_upnl_usdt from lib.hub.hub_position_metrics import estimate_linear_swap_upnl_usdt
return estimate_linear_swap_upnl_usdt( return estimate_linear_swap_upnl_usdt(
direction, float(avg_entry), float(take_profit_price), float(contracts), float(contract_size) direction, float(avg_entry), float(take_profit_price), float(contracts), float(contract_size)
@@ -11,16 +11,16 @@ from typing import Any, Optional
from flask import Flask, flash, redirect, request, url_for from flask import Flask, flash, redirect, request, url_for
from jinja2 import ChoiceLoader, FileSystemLoader from jinja2 import ChoiceLoader, FileSystemLoader
from strategy_config import resolve_trading_app_module from lib.strategy.strategy_config import resolve_trading_app_module
from strategy_db import init_strategy_tables from lib.strategy.strategy_db import init_strategy_tables
from strategy_trend_exchange import ( from lib.strategy.strategy_trend_exchange import (
cancel_symbol_orders, cancel_symbol_orders,
trend_market_add, trend_market_add,
trend_market_close, trend_market_close,
trend_refresh_stop_only, trend_refresh_stop_only,
trend_replace_tpsl, trend_replace_tpsl,
) )
from strategy_trend_lib import ( from lib.strategy.strategy_trend_lib import (
build_grid_prices, build_grid_prices,
build_leg_amounts_json, build_leg_amounts_json,
calc_risk_fraction, calc_risk_fraction,
@@ -28,7 +28,7 @@ from strategy_trend_lib import (
trend_effective_margin_capital, trend_effective_margin_capital,
validate_trend_bounds, validate_trend_bounds,
) )
from strategy_trade_labels import ( from lib.strategy.strategy_trade_labels import (
ENTRY_REASON_TREND_PULLBACK, ENTRY_REASON_TREND_PULLBACK,
MONITOR_TYPE_TREND_PULLBACK, MONITOR_TYPE_TREND_PULLBACK,
TREND_HANDOFF_KEY_SIGNAL, TREND_HANDOFF_KEY_SIGNAL,
@@ -151,7 +151,7 @@ def trend_add_zone_label(direction: str) -> str:
def install_strategy_trend(app: Flask, repo_root: str, app_module: Any = None, **build_kw) -> dict: def install_strategy_trend(app: Flask, repo_root: str, app_module: Any = None, **build_kw) -> dict:
from strategy_register import attach_strategy_templates from lib.strategy.strategy_register import attach_strategy_templates
attach_strategy_templates(app, repo_root) attach_strategy_templates(app, repo_root)
cfg = build_trend_config(app_module, **build_kw) cfg = build_trend_config(app_module, **build_kw)
@@ -160,7 +160,7 @@ def install_strategy_trend(app: Flask, repo_root: str, app_module: Any = None, *
_patch_hub_monitor_enrich(app, cfg) _patch_hub_monitor_enrich(app, cfg)
roll_cfg = app.extensions.get("strategy_roll_cfg") roll_cfg = app.extensions.get("strategy_roll_cfg")
if isinstance(roll_cfg, dict): if isinstance(roll_cfg, dict):
from strategy_roll_ui_lib import patch_roll_hub_enrich from lib.strategy.strategy_roll_ui_lib import patch_roll_hub_enrich
patch_roll_hub_enrich(app, roll_cfg) patch_roll_hub_enrich(app, roll_cfg)
_patch_hub_trend_views(app) _patch_hub_trend_views(app)
@@ -248,7 +248,7 @@ def precheck_trend_start(cfg: dict, conn) -> tuple[bool, str]:
m = _m(cfg) m = _m(cfg)
mode = getattr(m, "POSITION_SIZING_MODE", None) or "risk" mode = getattr(m, "POSITION_SIZING_MODE", None) or "risk"
try: try:
from position_sizing_lib import OPEN_SOURCE_TREND, assert_open_source_allowed from lib.trade.position_sizing_lib import OPEN_SOURCE_TREND, assert_open_source_allowed
ok_src, src_msg = assert_open_source_allowed(mode, OPEN_SOURCE_TREND) ok_src, src_msg = assert_open_source_allowed(mode, OPEN_SOURCE_TREND)
if not ok_src: if not ok_src:
@@ -462,7 +462,7 @@ def _trend_add_leg_fields(cfg: dict, d: dict) -> dict:
grid = [] grid = []
add_prices: list[float] = [] add_prices: list[float] = []
try: try:
from strategy_trend_lib import trend_leg_display_price from lib.strategy.strategy_trend_lib import trend_leg_display_price
for i in range(1, legs_done + 1): for i in range(1, legs_done + 1):
px = trend_leg_display_price(out, i) px = trend_leg_display_price(out, i)
@@ -582,7 +582,7 @@ def enrich_trend_plan(cfg: dict, row) -> dict:
and d.get("avg_entry_price") is not None and d.get("avg_entry_price") is not None
): ):
try: try:
from hub_position_metrics import estimate_linear_swap_upnl_usdt from lib.hub.hub_position_metrics import estimate_linear_swap_upnl_usdt
entry = float(d["avg_entry_price"]) entry = float(d["avg_entry_price"])
mark = float(met["mark_price"]) mark = float(met["mark_price"])
@@ -615,8 +615,8 @@ def enrich_trend_plan(cfg: dict, row) -> dict:
except (TypeError, ValueError): except (TypeError, ValueError):
pass pass
d = _trend_add_leg_fields(cfg, d) d = _trend_add_leg_fields(cfg, d)
from strategy_snapshot_lib import attach_trend_dca_levels from lib.strategy.strategy_snapshot_lib import attach_trend_dca_levels
from strategy_trend_lib import calc_trend_plan_money_metrics from lib.strategy.strategy_trend_lib import calc_trend_plan_money_metrics
d = attach_trend_dca_levels(d) d = attach_trend_dca_levels(d)
money = calc_trend_plan_money_metrics(d) money = calc_trend_plan_money_metrics(d)
@@ -663,7 +663,7 @@ def _call_insert_trade_record(m, plan_id: int, kwargs: dict) -> None:
def _best_trend_close_snapshot(conn, plan_id: int) -> dict | None: def _best_trend_close_snapshot(conn, plan_id: int) -> dict | None:
from strategy_snapshot_lib import ( from lib.strategy.strategy_snapshot_lib import (
FINAL_TREND_CLOSE_LABELS, FINAL_TREND_CLOSE_LABELS,
STRATEGY_TREND, STRATEGY_TREND,
_final_trend_close_rank, _final_trend_close_rank,
@@ -846,7 +846,7 @@ def _bump_session_capital_no_commit(
def _apply_trend_user_risk_close(cfg: dict, conn, *, trade_record_id=None, closed_at_ms=None) -> None: def _apply_trend_user_risk_close(cfg: dict, conn, *, trade_record_id=None, closed_at_ms=None) -> None:
m = _m(cfg) m = _m(cfg)
fn = getattr(m, "hub_user_initiated_close", None) fn = getattr(m, "hub_user_initiated_close", None)
from account_risk_lib import CLOSE_SOURCE_USER_TREND_STOP from lib.trade.account_risk_lib import CLOSE_SOURCE_USER_TREND_STOP
if callable(fn): if callable(fn):
fn( fn(
@@ -857,7 +857,7 @@ def _apply_trend_user_risk_close(cfg: dict, conn, *, trade_record_id=None, close
closed_at_ms=closed_at_ms, closed_at_ms=closed_at_ms,
) )
return return
from account_risk_lib import on_user_initiated_close from lib.trade.account_risk_lib import on_user_initiated_close
on_user_initiated_close( on_user_initiated_close(
conn, conn,
@@ -911,7 +911,7 @@ def _finalize_plan(cfg: dict, conn, row, result_label: str, exit_price: float, *
if not getattr(cur, "rowcount", 0): if not getattr(cur, "rowcount", 0):
return return
try: try:
from strategy_snapshot_lib import save_trend_plan_snapshot from lib.strategy.strategy_snapshot_lib import save_trend_plan_snapshot
save_trend_plan_snapshot( save_trend_plan_snapshot(
cfg, cfg,
@@ -962,7 +962,7 @@ def _finalize_plan(cfg: dict, conn, row, result_label: str, exit_price: float, *
), ),
) )
try: try:
from account_risk_lib import insert_trade_record_id from lib.trade.account_risk_lib import insert_trade_record_id
trade_record_id = insert_trade_record_id(conn) trade_record_id = insert_trade_record_id(conn)
except Exception: except Exception:
@@ -983,7 +983,7 @@ def _finalize_plan(cfg: dict, conn, row, result_label: str, exit_price: float, *
) )
conn.commit() conn.commit()
try: try:
from strategy_wechat_notify import notify_trend_plan_ended from lib.strategy.strategy_wechat_notify import notify_trend_plan_ended
notify_trend_plan_ended( notify_trend_plan_ended(
cfg, cfg,
@@ -1220,7 +1220,7 @@ def check_trend_pullback_plans(cfg: dict) -> None:
old_open = float(row["order_amount_open"] or 0) old_open = float(row["order_amount_open"] or 0)
new_avg = _weighted_avg(old_avg, old_open, fill_px, amt) new_avg = _weighted_avg(old_avg, old_open, fill_px, amt)
legs_done += 1 legs_done += 1
from strategy_trend_lib import append_leg_fill_price_json from lib.strategy.strategy_trend_lib import append_leg_fill_price_json
fills_json = append_leg_fill_price_json( fills_json = append_leg_fill_price_json(
row["leg_fill_prices_json"] if "leg_fill_prices_json" in row.keys() else None, row["leg_fill_prices_json"] if "leg_fill_prices_json" in row.keys() else None,
@@ -1428,7 +1428,7 @@ def apply_manual_breakeven(cfg: dict, conn, row, offset_pct=None) -> tuple[bool,
return False, live_reason or "实盘未就绪" return False, live_reason or "实盘未就绪"
plan_id = int(row["id"]) plan_id = int(row["id"])
try: try:
from strategy_snapshot_lib import save_trend_plan_snapshot from lib.strategy.strategy_snapshot_lib import save_trend_plan_snapshot
save_trend_plan_snapshot( save_trend_plan_snapshot(
cfg, conn, row, result_label="保本移交", exit_price=None, pnl_amount=None cfg, conn, row, result_label="保本移交", exit_price=None, pnl_amount=None
@@ -1479,7 +1479,7 @@ def apply_manual_breakeven(cfg: dict, conn, row, offset_pct=None) -> tuple[bool,
pf = getattr(m, "format_price_for_symbol", None) pf = getattr(m, "format_price_for_symbol", None)
fmt = (lambda s, p: pf(s, p)) if callable(pf) else (lambda _s, p: str(p)) fmt = (lambda s, p: pf(s, p)) if callable(pf) else (lambda _s, p: str(p))
try: try:
from strategy_wechat_notify import notify_trend_plan_ended from lib.strategy.strategy_wechat_notify import notify_trend_plan_ended
notify_trend_plan_ended( notify_trend_plan_ended(
cfg, cfg,
@@ -1537,7 +1537,7 @@ def load_trend_page_context(conn, request_obj, cfg: dict) -> dict[str, Any]:
pass pass
now = m.app_now() now = m.app_now()
active_count = m.get_active_position_count(conn) active_count = m.get_active_position_count(conn)
from daily_open_limit_lib import can_trade_new_open, count_opens_for_trading_day from lib.trade.daily_open_limit_lib import can_trade_new_open, count_opens_for_trading_day
trading_day = m.get_trading_day(now) trading_day = m.get_trading_day(now)
opens_today = count_opens_for_trading_day(conn, trading_day) opens_today = count_opens_for_trading_day(conn, trading_day)
@@ -1561,7 +1561,7 @@ def load_trend_page_context(conn, request_obj, cfg: dict) -> dict[str, Any]:
).fetchone() ).fetchone()
now_ms = int(time.time() * 1000) now_ms = int(time.time() * 1000)
if pr and int(pr["expires_at_ms"] or 0) >= now_ms: if pr and int(pr["expires_at_ms"] or 0) >= now_ms:
from strategy_trend_lib import build_trend_preview_level_rows from lib.strategy.strategy_trend_lib import build_trend_preview_level_rows
trend_preview = _row(cfg, pr) trend_preview = _row(cfg, pr)
preview_expires_ms = int(pr["expires_at_ms"]) preview_expires_ms = int(pr["expires_at_ms"])
@@ -1727,7 +1727,7 @@ def register_trend_routes(app: Flask, cfg: dict) -> None:
trading_day = m.get_trading_day(m.app_now()) trading_day = m.get_trading_day(m.app_now())
opened_at = m.app_now_str() opened_at = m.app_now_str()
opened_ms = getattr(m, "_to_ms_with_fallback", lambda a, b: None)(None, opened_at) opened_ms = getattr(m, "_to_ms_with_fallback", lambda a, b: None)(None, opened_at)
from strategy_trend_lib import append_leg_fill_price_json from lib.strategy.strategy_trend_lib import append_leg_fill_price_json
fills_json = append_leg_fill_price_json(None, fill1) fills_json = append_leg_fill_price_json(None, fill1)
cur = conn.execute( cur = conn.execute(
@@ -1777,7 +1777,7 @@ def register_trend_routes(app: Flask, cfg: dict) -> None:
conn.execute("DELETE FROM trend_pullback_previews WHERE id=?", (pid,)) conn.execute("DELETE FROM trend_pullback_previews WHERE id=?", (pid,))
conn.commit() conn.commit()
try: try:
from strategy_wechat_notify import notify_trend_plan_started from lib.strategy.strategy_wechat_notify import notify_trend_plan_started
notify_trend_plan_started( notify_trend_plan_started(
cfg, cfg,
@@ -3,8 +3,8 @@ from __future__ import annotations
from typing import Any, Callable, Optional from typing import Any, Callable, Optional
from strategy_db import init_strategy_tables from lib.strategy.strategy_db import init_strategy_tables
from strategy_roll_monitor_lib import roll_leg_status_label from lib.strategy.strategy_roll_monitor_lib import roll_leg_status_label
def _row_to_dict(row) -> dict: def _row_to_dict(row) -> dict:
@@ -70,7 +70,7 @@ def fetch_roll_page_data(
"default_risk_percent": default_risk_percent, "default_risk_percent": default_risk_percent,
} }
if roll_cfg: if roll_cfg:
from strategy_roll_ui_lib import enrich_roll_page_data from lib.strategy.strategy_roll_ui_lib import enrich_roll_page_data
enrich_roll_page_data(conn, out, roll_cfg) enrich_roll_page_data(conn, out, roll_cfg)
return out return out
@@ -95,7 +95,7 @@ def strategy_render_extras(
) -> dict[str, Any]: ) -> dict[str, Any]:
"""render_main_page 策略相关页变量(含策略交易记录)。""" """render_main_page 策略相关页变量(含策略交易记录)。"""
if page == "strategy_records": if page == "strategy_records":
from strategy_records_register import load_strategy_records_page from lib.strategy.strategy_records_register import load_strategy_records_page
return load_strategy_records_page(conn) return load_strategy_records_page(conn)
return strategy_page_template_vars( return strategy_page_template_vars(
@@ -136,7 +136,7 @@ def strategy_page_template_vars(
roll_cfg=roll_cfg if isinstance(roll_cfg, dict) else None, roll_cfg=roll_cfg if isinstance(roll_cfg, dict) else None,
) )
if trend_cfg and request_obj is not None: if trend_cfg and request_obj is not None:
from strategy_trend_register import load_trend_page_context from lib.strategy.strategy_trend_register import load_trend_page_context
out.update(load_trend_page_context(conn, request_obj, trend_cfg)) out.update(load_trend_page_context(conn, request_obj, trend_cfg))
elif page == "strategy_trend": elif page == "strategy_trend":
@@ -3,7 +3,7 @@ from __future__ import annotations
from typing import Any, Optional from typing import Any, Optional
from wechat_notify_lib import wechat_direction_label from lib.common.wechat_notify_lib import wechat_direction_label
def _send(cfg: dict[str, Any], content: str) -> None: def _send(cfg: dict[str, Any], content: str) -> None:

Some files were not shown because too many files have changed in this diff Show More