修复复盘
This commit is contained in:
@@ -59,8 +59,10 @@ from history_window_lib import (
|
|||||||
PRESET_UTC_LAST7D,
|
PRESET_UTC_LAST7D,
|
||||||
PRESET_UTC_TODAY,
|
PRESET_UTC_TODAY,
|
||||||
list_window_redirect_query,
|
list_window_redirect_query,
|
||||||
|
normalize_bj_datetime_storage,
|
||||||
resolve_list_window,
|
resolve_list_window,
|
||||||
resolve_window,
|
resolve_window,
|
||||||
|
sql_list_time_field,
|
||||||
utc_window_to_bj_sql_strings,
|
utc_window_to_bj_sql_strings,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -5653,9 +5655,9 @@ def render_main_page(page="trade"):
|
|||||||
order_list = []
|
order_list = []
|
||||||
for o in raw_order_list:
|
for o in raw_order_list:
|
||||||
order_list.append(enrich_order_item(row_to_dict(o), current_capital))
|
order_list.append(enrich_order_item(row_to_dict(o), current_capital))
|
||||||
|
tr_ts = sql_list_time_field("closed_at", "created_at", "opened_at")
|
||||||
raw_records = conn.execute(
|
raw_records = conn.execute(
|
||||||
"SELECT * FROM trade_records WHERE COALESCE(closed_at, created_at, opened_at) >= ? "
|
f"SELECT * FROM trade_records WHERE {tr_ts} >= ? AND {tr_ts} <= ? ORDER BY id DESC LIMIT 1000",
|
||||||
"AND COALESCE(closed_at, created_at, opened_at) <= ? ORDER BY id DESC LIMIT 1000",
|
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
records = [to_effective_trade_dict(r) for r in raw_records]
|
records = [to_effective_trade_dict(r) for r in raw_records]
|
||||||
@@ -6834,8 +6836,8 @@ def export_trade_records():
|
|||||||
"margin_capital,leverage,pnl_amount,hold_seconds,hold_minutes,planned_rr,actual_rr,risk_amount,"
|
"margin_capital,leverage,pnl_amount,hold_seconds,hold_minutes,planned_rr,actual_rr,risk_amount,"
|
||||||
"opened_at,closed_at,result,miss_reason,entry_reason,reviewed_entry_reason,"
|
"opened_at,closed_at,result,miss_reason,entry_reason,reviewed_entry_reason,"
|
||||||
"exchange_realized_pnl,exchange_opened_at,exchange_closed_at,created_at "
|
"exchange_realized_pnl,exchange_opened_at,exchange_closed_at,created_at "
|
||||||
"FROM trade_records WHERE COALESCE(closed_at, created_at, opened_at) >= ? "
|
f"FROM trade_records WHERE {sql_list_time_field('closed_at', 'created_at', 'opened_at')} >= ? "
|
||||||
"AND COALESCE(closed_at, created_at, opened_at) <= ? ORDER BY id ASC",
|
f"AND {sql_list_time_field('closed_at', 'created_at', 'opened_at')} <= ? ORDER BY id ASC",
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
@@ -7239,7 +7241,12 @@ def add_journal():
|
|||||||
new_trade_while_occupied, note, image)
|
new_trade_while_occupied, note, image)
|
||||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||||
(
|
(
|
||||||
entry_id, d.get("open_datetime"), d.get("close_datetime"), hold_duration, d.get("coin"), d.get("tf"),
|
entry_id,
|
||||||
|
normalize_bj_datetime_storage(d.get("open_datetime")),
|
||||||
|
normalize_bj_datetime_storage(d.get("close_datetime")),
|
||||||
|
hold_duration,
|
||||||
|
d.get("coin"),
|
||||||
|
d.get("tf"),
|
||||||
d.get("pnl"), entry_reason_norm, exit_reason_stored, d.get("expect_rr"), real_rr_text,
|
d.get("pnl"), entry_reason_norm, exit_reason_stored, d.get("expect_rr"), real_rr_text,
|
||||||
early_exit_raw, early_exit_reason_saved, early_exit_trigger, early_exit_note,
|
early_exit_raw, early_exit_reason_saved, early_exit_trigger, early_exit_note,
|
||||||
None, None, None, mood_issues,
|
None, None, None, mood_issues,
|
||||||
@@ -7261,9 +7268,9 @@ def api_journals():
|
|||||||
win = _list_window_from_request()
|
win = _list_window_from_request()
|
||||||
start_bj, end_bj = utc_window_to_bj_sql_strings(win["start_utc"], win["end_utc"], APP_TZ)
|
start_bj, end_bj = utc_window_to_bj_sql_strings(win["start_utc"], win["end_utc"], APP_TZ)
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
|
j_ts = sql_list_time_field("close_datetime", "created_at", "open_datetime")
|
||||||
rows = conn.execute(
|
rows = conn.execute(
|
||||||
"SELECT * FROM journal_entries WHERE COALESCE(close_datetime, created_at, open_datetime) >= ? "
|
f"SELECT * FROM journal_entries WHERE {j_ts} >= ? AND {j_ts} <= ? ORDER BY created_at DESC LIMIT 500",
|
||||||
"AND COALESCE(close_datetime, created_at, open_datetime) <= ? ORDER BY created_at DESC LIMIT 500",
|
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|||||||
@@ -60,8 +60,10 @@ from history_window_lib import (
|
|||||||
PRESET_UTC_LAST7D,
|
PRESET_UTC_LAST7D,
|
||||||
PRESET_UTC_TODAY,
|
PRESET_UTC_TODAY,
|
||||||
list_window_redirect_query,
|
list_window_redirect_query,
|
||||||
|
normalize_bj_datetime_storage,
|
||||||
resolve_list_window,
|
resolve_list_window,
|
||||||
resolve_window,
|
resolve_window,
|
||||||
|
sql_list_time_field,
|
||||||
utc_window_to_bj_sql_strings,
|
utc_window_to_bj_sql_strings,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -5632,9 +5634,9 @@ def render_main_page(page="trade"):
|
|||||||
exchange_pnl_sync = sync_trade_records_from_exchange(conn) or {}
|
exchange_pnl_sync = sync_trade_records_from_exchange(conn) or {}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
exchange_pnl_sync = {"ok": False, "reason": str(e)}
|
exchange_pnl_sync = {"ok": False, "reason": str(e)}
|
||||||
|
tr_ts = sql_list_time_field("closed_at", "created_at", "opened_at")
|
||||||
raw_records = conn.execute(
|
raw_records = conn.execute(
|
||||||
"SELECT * FROM trade_records WHERE COALESCE(closed_at, created_at, opened_at) >= ? "
|
f"SELECT * FROM trade_records WHERE {tr_ts} >= ? AND {tr_ts} <= ? ORDER BY id DESC LIMIT 1000",
|
||||||
"AND COALESCE(closed_at, created_at, opened_at) <= ? ORDER BY id DESC LIMIT 1000",
|
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
records = [to_effective_trade_dict(r) for r in raw_records]
|
records = [to_effective_trade_dict(r) for r in raw_records]
|
||||||
@@ -6879,8 +6881,8 @@ def export_trade_records():
|
|||||||
"margin_capital,leverage,pnl_amount,hold_seconds,hold_minutes,planned_rr,actual_rr,risk_amount,"
|
"margin_capital,leverage,pnl_amount,hold_seconds,hold_minutes,planned_rr,actual_rr,risk_amount,"
|
||||||
"opened_at,closed_at,result,miss_reason,entry_reason,reviewed_entry_reason,"
|
"opened_at,closed_at,result,miss_reason,entry_reason,reviewed_entry_reason,"
|
||||||
"exchange_realized_pnl,exchange_opened_at,exchange_closed_at,created_at "
|
"exchange_realized_pnl,exchange_opened_at,exchange_closed_at,created_at "
|
||||||
"FROM trade_records WHERE COALESCE(closed_at, created_at, opened_at) >= ? "
|
f"FROM trade_records WHERE {sql_list_time_field('closed_at', 'created_at', 'opened_at')} >= ? "
|
||||||
"AND COALESCE(closed_at, created_at, opened_at) <= ? ORDER BY id ASC",
|
f"AND {sql_list_time_field('closed_at', 'created_at', 'opened_at')} <= ? ORDER BY id ASC",
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
@@ -7265,7 +7267,12 @@ def add_journal():
|
|||||||
new_trade_while_occupied, note, image)
|
new_trade_while_occupied, note, image)
|
||||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||||
(
|
(
|
||||||
entry_id, d.get("open_datetime"), d.get("close_datetime"), hold_duration, d.get("coin"), d.get("tf"),
|
entry_id,
|
||||||
|
normalize_bj_datetime_storage(d.get("open_datetime")),
|
||||||
|
normalize_bj_datetime_storage(d.get("close_datetime")),
|
||||||
|
hold_duration,
|
||||||
|
d.get("coin"),
|
||||||
|
d.get("tf"),
|
||||||
d.get("pnl"), entry_reason_norm, exit_reason_stored, d.get("expect_rr"), real_rr_text,
|
d.get("pnl"), entry_reason_norm, exit_reason_stored, d.get("expect_rr"), real_rr_text,
|
||||||
early_exit_raw, early_exit_reason_saved, early_exit_trigger, early_exit_note,
|
early_exit_raw, early_exit_reason_saved, early_exit_trigger, early_exit_note,
|
||||||
None, None, None, mood_issues,
|
None, None, None, mood_issues,
|
||||||
@@ -7287,9 +7294,9 @@ def api_journals():
|
|||||||
win = _list_window_from_request()
|
win = _list_window_from_request()
|
||||||
start_bj, end_bj = utc_window_to_bj_sql_strings(win["start_utc"], win["end_utc"], APP_TZ)
|
start_bj, end_bj = utc_window_to_bj_sql_strings(win["start_utc"], win["end_utc"], APP_TZ)
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
|
j_ts = sql_list_time_field("close_datetime", "created_at", "open_datetime")
|
||||||
rows = conn.execute(
|
rows = conn.execute(
|
||||||
"SELECT * FROM journal_entries WHERE COALESCE(close_datetime, created_at, open_datetime) >= ? "
|
f"SELECT * FROM journal_entries WHERE {j_ts} >= ? AND {j_ts} <= ? ORDER BY created_at DESC LIMIT 500",
|
||||||
"AND COALESCE(close_datetime, created_at, open_datetime) <= ? ORDER BY created_at DESC LIMIT 500",
|
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|||||||
@@ -40,8 +40,10 @@ from history_window_lib import (
|
|||||||
PRESET_UTC_LAST7D,
|
PRESET_UTC_LAST7D,
|
||||||
PRESET_UTC_TODAY,
|
PRESET_UTC_TODAY,
|
||||||
list_window_redirect_query,
|
list_window_redirect_query,
|
||||||
|
normalize_bj_datetime_storage,
|
||||||
resolve_list_window,
|
resolve_list_window,
|
||||||
resolve_window,
|
resolve_window,
|
||||||
|
sql_list_time_field,
|
||||||
utc_window_to_bj_sql_strings,
|
utc_window_to_bj_sql_strings,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -5213,8 +5215,8 @@ def render_main_page(page="trade"):
|
|||||||
pass
|
pass
|
||||||
if page in ("records", "plan_history"):
|
if page in ("records", "plan_history"):
|
||||||
raw_records = conn.execute(
|
raw_records = conn.execute(
|
||||||
"SELECT * FROM trade_records WHERE COALESCE(closed_at, created_at, opened_at) >= ? "
|
f"SELECT * FROM trade_records WHERE {sql_list_time_field('closed_at', 'created_at', 'opened_at')} >= ? "
|
||||||
"AND COALESCE(closed_at, created_at, opened_at) <= ? ORDER BY id DESC LIMIT 2000",
|
f"AND {sql_list_time_field('closed_at', 'created_at', 'opened_at')} <= ? ORDER BY id DESC LIMIT 2000",
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
else:
|
else:
|
||||||
@@ -6542,8 +6544,8 @@ def export_trade_records():
|
|||||||
"pnl_amount,hold_seconds,hold_minutes,opened_at,closed_at,result,miss_reason,"
|
"pnl_amount,hold_seconds,hold_minutes,opened_at,closed_at,result,miss_reason,"
|
||||||
"entry_reason,reviewed_entry_reason,created_at,trend_plan_id,exchange_realized_pnl,"
|
"entry_reason,reviewed_entry_reason,created_at,trend_plan_id,exchange_realized_pnl,"
|
||||||
"exchange_opened_at,exchange_closed_at,exchange_sync_key FROM trade_records "
|
"exchange_opened_at,exchange_closed_at,exchange_sync_key FROM trade_records "
|
||||||
"WHERE COALESCE(closed_at, created_at, opened_at) >= ? "
|
f"WHERE {sql_list_time_field('closed_at', 'created_at', 'opened_at')} >= ? "
|
||||||
"AND COALESCE(closed_at, created_at, opened_at) <= ? ORDER BY id ASC",
|
f"AND {sql_list_time_field('closed_at', 'created_at', 'opened_at')} <= ? ORDER BY id ASC",
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
@@ -6916,7 +6918,12 @@ def add_journal():
|
|||||||
new_trade_while_occupied, note, image)
|
new_trade_while_occupied, note, image)
|
||||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||||
(
|
(
|
||||||
entry_id, d.get("open_datetime"), d.get("close_datetime"), hold_duration, d.get("coin"), d.get("tf"),
|
entry_id,
|
||||||
|
normalize_bj_datetime_storage(d.get("open_datetime")),
|
||||||
|
normalize_bj_datetime_storage(d.get("close_datetime")),
|
||||||
|
hold_duration,
|
||||||
|
d.get("coin"),
|
||||||
|
d.get("tf"),
|
||||||
d.get("pnl"), entry_reason_norm, exit_reason_stored, d.get("expect_rr"), real_rr_text,
|
d.get("pnl"), entry_reason_norm, exit_reason_stored, d.get("expect_rr"), real_rr_text,
|
||||||
early_exit_raw, early_exit_reason_saved, early_exit_trigger, early_exit_note,
|
early_exit_raw, early_exit_reason_saved, early_exit_trigger, early_exit_note,
|
||||||
None, None, None, mood_issues,
|
None, None, None, mood_issues,
|
||||||
@@ -6939,8 +6946,8 @@ def api_journals():
|
|||||||
start_bj, end_bj = utc_window_to_bj_sql_strings(win["start_utc"], win["end_utc"], APP_TZ)
|
start_bj, end_bj = utc_window_to_bj_sql_strings(win["start_utc"], win["end_utc"], APP_TZ)
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
rows = conn.execute(
|
rows = conn.execute(
|
||||||
"SELECT * FROM journal_entries WHERE COALESCE(close_datetime, created_at, open_datetime) >= ? "
|
f"SELECT * FROM journal_entries WHERE {sql_list_time_field('close_datetime', 'created_at', 'open_datetime')} >= ? "
|
||||||
"AND COALESCE(close_datetime, created_at, open_datetime) <= ? ORDER BY created_at DESC LIMIT 500",
|
f"AND {sql_list_time_field('close_datetime', 'created_at', 'open_datetime')} <= ? ORDER BY created_at DESC LIMIT 500",
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|||||||
@@ -59,8 +59,10 @@ from history_window_lib import (
|
|||||||
PRESET_UTC_LAST7D,
|
PRESET_UTC_LAST7D,
|
||||||
PRESET_UTC_TODAY,
|
PRESET_UTC_TODAY,
|
||||||
list_window_redirect_query,
|
list_window_redirect_query,
|
||||||
|
normalize_bj_datetime_storage,
|
||||||
resolve_list_window,
|
resolve_list_window,
|
||||||
resolve_window,
|
resolve_window,
|
||||||
|
sql_list_time_field,
|
||||||
utc_window_to_bj_sql_strings,
|
utc_window_to_bj_sql_strings,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -4166,8 +4168,8 @@ def render_main_page(page="trade"):
|
|||||||
for o in raw_order_list:
|
for o in raw_order_list:
|
||||||
order_list.append(enrich_order_item(row_to_dict(o), current_capital))
|
order_list.append(enrich_order_item(row_to_dict(o), current_capital))
|
||||||
raw_records = conn.execute(
|
raw_records = conn.execute(
|
||||||
"SELECT * FROM trade_records WHERE COALESCE(closed_at, created_at, opened_at) >= ? "
|
f"SELECT * FROM trade_records WHERE {sql_list_time_field('closed_at', 'created_at', 'opened_at')} >= ? "
|
||||||
"AND COALESCE(closed_at, created_at, opened_at) <= ? ORDER BY id DESC LIMIT 1000",
|
f"AND {sql_list_time_field('closed_at', 'created_at', 'opened_at')} <= ? ORDER BY id DESC LIMIT 1000",
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
records = [to_effective_trade_dict(r) for r in raw_records]
|
records = [to_effective_trade_dict(r) for r in raw_records]
|
||||||
@@ -5139,8 +5141,8 @@ def export_trade_records():
|
|||||||
"SELECT id,symbol,monitor_type,key_signal_type,direction,trigger_price,stop_loss,initial_stop_loss,take_profit,"
|
"SELECT id,symbol,monitor_type,key_signal_type,direction,trigger_price,stop_loss,initial_stop_loss,take_profit,"
|
||||||
"margin_capital,leverage,pnl_amount,hold_seconds,hold_minutes,planned_rr,actual_rr,risk_amount,"
|
"margin_capital,leverage,pnl_amount,hold_seconds,hold_minutes,planned_rr,actual_rr,risk_amount,"
|
||||||
"opened_at,closed_at,result,miss_reason,entry_reason,reviewed_entry_reason,created_at "
|
"opened_at,closed_at,result,miss_reason,entry_reason,reviewed_entry_reason,created_at "
|
||||||
"FROM trade_records WHERE COALESCE(closed_at, created_at, opened_at) >= ? "
|
f"FROM trade_records WHERE {sql_list_time_field('closed_at', 'created_at', 'opened_at')} >= ? "
|
||||||
"AND COALESCE(closed_at, created_at, opened_at) <= ? ORDER BY id ASC",
|
f"AND {sql_list_time_field('closed_at', 'created_at', 'opened_at')} <= ? ORDER BY id ASC",
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
@@ -5502,7 +5504,12 @@ def add_journal():
|
|||||||
new_trade_while_occupied, note, image)
|
new_trade_while_occupied, note, image)
|
||||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||||
(
|
(
|
||||||
entry_id, d.get("open_datetime"), d.get("close_datetime"), hold_duration, d.get("coin"), d.get("tf"),
|
entry_id,
|
||||||
|
normalize_bj_datetime_storage(d.get("open_datetime")),
|
||||||
|
normalize_bj_datetime_storage(d.get("close_datetime")),
|
||||||
|
hold_duration,
|
||||||
|
d.get("coin"),
|
||||||
|
d.get("tf"),
|
||||||
d.get("pnl"), entry_reason_norm, exit_reason_stored, d.get("expect_rr"), real_rr_text,
|
d.get("pnl"), entry_reason_norm, exit_reason_stored, d.get("expect_rr"), real_rr_text,
|
||||||
early_exit_raw, early_exit_reason_saved, early_exit_trigger, early_exit_note,
|
early_exit_raw, early_exit_reason_saved, early_exit_trigger, early_exit_note,
|
||||||
None, None, None, mood_issues,
|
None, None, None, mood_issues,
|
||||||
@@ -5525,8 +5532,8 @@ def api_journals():
|
|||||||
start_bj, end_bj = utc_window_to_bj_sql_strings(win["start_utc"], win["end_utc"], APP_TZ)
|
start_bj, end_bj = utc_window_to_bj_sql_strings(win["start_utc"], win["end_utc"], APP_TZ)
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
rows = conn.execute(
|
rows = conn.execute(
|
||||||
"SELECT * FROM journal_entries WHERE COALESCE(close_datetime, created_at, open_datetime) >= ? "
|
f"SELECT * FROM journal_entries WHERE {sql_list_time_field('close_datetime', 'created_at', 'open_datetime')} >= ? "
|
||||||
"AND COALESCE(close_datetime, created_at, open_datetime) <= ? ORDER BY created_at DESC LIMIT 500",
|
f"AND {sql_list_time_field('close_datetime', 'created_at', 'open_datetime')} <= ? ORDER BY created_at DESC LIMIT 500",
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|||||||
+210
-210
@@ -1,210 +1,210 @@
|
|||||||
#Requires -Version 5.1
|
#Requires -Version 5.1
|
||||||
<#
|
<#
|
||||||
.SYNOPSIS
|
.SYNOPSIS
|
||||||
crypto_monitor 一键环境部署(Windows PowerShell)
|
crypto_monitor 一键环境部署(Windows PowerShell)
|
||||||
|
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
- 为各子项目创建 Python venv 并安装依赖
|
- 为各子项目创建 Python venv 并安装依赖
|
||||||
- 从 .env.example 复制 .env(不覆盖已有)
|
- 从 .env.example 复制 .env(不覆盖已有)
|
||||||
- 创建 static/images 等运行时目录
|
- 创建 static/images 等运行时目录
|
||||||
- 可选安装 PM2(需已安装 Node.js)
|
- 可选安装 PM2(需已安装 Node.js)
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
.\deploy\setup_env.ps1
|
.\deploy\setup_env.ps1
|
||||||
.\deploy\setup_env.ps1 -Only binance,gate_bot
|
.\deploy\setup_env.ps1 -Only binance,gate_bot
|
||||||
.\deploy\setup_env.ps1 -SkipPm2
|
.\deploy\setup_env.ps1 -SkipPm2
|
||||||
#>
|
#>
|
||||||
param(
|
param(
|
||||||
[string]$Only = "all",
|
[string]$Only = "all",
|
||||||
[switch]$SkipPm2,
|
[switch]$SkipPm2,
|
||||||
[switch]$SkipEnvCopy,
|
[switch]$SkipEnvCopy,
|
||||||
[switch]$RecreateVenv
|
[switch]$RecreateVenv
|
||||||
)
|
)
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
|
||||||
$DeployDir = $PSScriptRoot
|
$DeployDir = $PSScriptRoot
|
||||||
$RepoRoot = (Resolve-Path (Join-Path $DeployDir "..")).Path
|
$RepoRoot = (Resolve-Path (Join-Path $DeployDir "..")).Path
|
||||||
$ReqFile = Join-Path $RepoRoot "requirements.txt"
|
$ReqFile = Join-Path $RepoRoot "requirements.txt"
|
||||||
$HubReqFile = Join-Path $RepoRoot "manual_trading_hub\requirements.txt"
|
$HubReqFile = Join-Path $RepoRoot "manual_trading_hub\requirements.txt"
|
||||||
|
|
||||||
$MonitorProjects = @(
|
$MonitorProjects = @(
|
||||||
@{ Key = "binance"; Dir = "crypto_monitor_binance" },
|
@{ Key = "binance"; Dir = "crypto_monitor_binance" },
|
||||||
@{ Key = "gate"; Dir = "crypto_monitor_gate" },
|
@{ Key = "gate"; Dir = "crypto_monitor_gate" },
|
||||||
@{ Key = "gate_bot"; Dir = "crypto_monitor_gate_bot" },
|
@{ Key = "gate_bot"; Dir = "crypto_monitor_gate_bot" },
|
||||||
@{ Key = "okx"; Dir = "crypto_monitor_okx" }
|
@{ Key = "okx"; Dir = "crypto_monitor_okx" }
|
||||||
)
|
)
|
||||||
$HubProject = @{ Key = "hub"; Dir = "manual_trading_hub" }
|
$HubProject = @{ Key = "hub"; Dir = "manual_trading_hub" }
|
||||||
|
|
||||||
function Write-Step([string]$Msg) {
|
function Write-Step([string]$Msg) {
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "==> $Msg" -ForegroundColor Cyan
|
Write-Host "==> $Msg" -ForegroundColor Cyan
|
||||||
}
|
}
|
||||||
|
|
||||||
function Test-Python310 {
|
function Test-Python310 {
|
||||||
$py = Get-Command python -ErrorAction SilentlyContinue
|
$py = Get-Command python -ErrorAction SilentlyContinue
|
||||||
if (-not $py) {
|
if (-not $py) {
|
||||||
throw "未找到 python。请安装 Python 3.10+ 并加入 PATH:https://www.python.org/downloads/"
|
throw "未找到 python。请安装 Python 3.10+ 并加入 PATH:https://www.python.org/downloads/"
|
||||||
}
|
}
|
||||||
$verText = & python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"
|
$verText = & python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"
|
||||||
$parts = $verText.Trim() -split "\."
|
$parts = $verText.Trim() -split "\."
|
||||||
$major = [int]$parts[0]
|
$major = [int]$parts[0]
|
||||||
$minor = [int]$parts[1]
|
$minor = [int]$parts[1]
|
||||||
if ($major -lt 3 -or ($major -eq 3 -and $minor -lt 10)) {
|
if ($major -lt 3 -or ($major -eq 3 -and $minor -lt 10)) {
|
||||||
throw "需要 Python 3.10+,当前: $verText"
|
throw "需要 Python 3.10+,当前: $verText"
|
||||||
}
|
}
|
||||||
Write-Host "Python: $(python --version 2>&1)" -ForegroundColor DarkGray
|
Write-Host "Python: $(python --version 2>&1)" -ForegroundColor DarkGray
|
||||||
}
|
}
|
||||||
|
|
||||||
function Should-Include([string]$Key, [string[]]$Selected) {
|
function Should-Include([string]$Key, [string[]]$Selected) {
|
||||||
if ($Selected -contains "all") { return $true }
|
if ($Selected -contains "all") { return $true }
|
||||||
return $Selected -contains $Key
|
return $Selected -contains $Key
|
||||||
}
|
}
|
||||||
|
|
||||||
function Setup-MonitorProject([hashtable]$Proj) {
|
function Setup-MonitorProject([hashtable]$Proj) {
|
||||||
$projPath = Join-Path $RepoRoot $Proj.Dir
|
$projPath = Join-Path $RepoRoot $Proj.Dir
|
||||||
if (-not (Test-Path $projPath)) {
|
if (-not (Test-Path $projPath)) {
|
||||||
Write-Host " 跳过(目录不存在): $($Proj.Dir)" -ForegroundColor Yellow
|
Write-Host " 跳过(目录不存在): $($Proj.Dir)" -ForegroundColor Yellow
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Write-Step "$($Proj.Dir)"
|
Write-Step "$($Proj.Dir)"
|
||||||
Push-Location $projPath
|
Push-Location $projPath
|
||||||
try {
|
try {
|
||||||
$venvDir = Join-Path $projPath ".venv"
|
$venvDir = Join-Path $projPath ".venv"
|
||||||
$venvPy = Join-Path $venvDir "Scripts\python.exe"
|
$venvPy = Join-Path $venvDir "Scripts\python.exe"
|
||||||
$venvPip = Join-Path $venvDir "Scripts\pip.exe"
|
$venvPip = Join-Path $venvDir "Scripts\pip.exe"
|
||||||
|
|
||||||
if ($RecreateVenv -and (Test-Path $venvDir)) {
|
if ($RecreateVenv -and (Test-Path $venvDir)) {
|
||||||
Write-Host " 删除旧 venv ..."
|
Write-Host " 删除旧 venv ..."
|
||||||
Remove-Item -Recurse -Force $venvDir
|
Remove-Item -Recurse -Force $venvDir
|
||||||
}
|
}
|
||||||
if (-not (Test-Path $venvPy)) {
|
if (-not (Test-Path $venvPy)) {
|
||||||
Write-Host " 创建 venv ..."
|
Write-Host " 创建 venv ..."
|
||||||
& python -m venv .venv
|
& python -m venv .venv
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host " 升级 pip ..."
|
Write-Host " 升级 pip ..."
|
||||||
& $venvPy -m pip install -U pip setuptools wheel -q
|
& $venvPy -m pip install -U pip setuptools wheel -q
|
||||||
|
|
||||||
Write-Host " 安装依赖 (requirements.txt) ..."
|
Write-Host " 安装依赖 (requirements.txt) ..."
|
||||||
& $venvPip install -r $ReqFile -q
|
& $venvPip install -r $ReqFile -q
|
||||||
|
|
||||||
if (-not $SkipEnvCopy) {
|
if (-not $SkipEnvCopy) {
|
||||||
$envExample = Join-Path $projPath ".env.example"
|
$envExample = Join-Path $projPath ".env.example"
|
||||||
$envFile = Join-Path $projPath ".env"
|
$envFile = Join-Path $projPath ".env"
|
||||||
if ((Test-Path $envExample) -and -not (Test-Path $envFile)) {
|
if ((Test-Path $envExample) -and -not (Test-Path $envFile)) {
|
||||||
Copy-Item $envExample $envFile
|
Copy-Item $envExample $envFile
|
||||||
Write-Host " 已复制 .env.example -> .env" -ForegroundColor Green
|
Write-Host " 已复制 .env.example -> .env" -ForegroundColor Green
|
||||||
} elseif (Test-Path $envFile) {
|
} elseif (Test-Path $envFile) {
|
||||||
Write-Host " 保留已有 .env" -ForegroundColor DarkGray
|
Write-Host " 保留已有 .env" -ForegroundColor DarkGray
|
||||||
} else {
|
} else {
|
||||||
Write-Host " 无 .env.example,请手动配置 .env" -ForegroundColor Yellow
|
Write-Host " 无 .env.example,请手动配置 .env" -ForegroundColor Yellow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$staticDirs = @(
|
$staticDirs = @(
|
||||||
"static\images",
|
"static\images",
|
||||||
"static\images\order_charts"
|
"static\images\order_charts"
|
||||||
)
|
)
|
||||||
foreach ($d in $staticDirs) {
|
foreach ($d in $staticDirs) {
|
||||||
$full = Join-Path $projPath $d
|
$full = Join-Path $projPath $d
|
||||||
if (-not (Test-Path $full)) {
|
if (-not (Test-Path $full)) {
|
||||||
New-Item -ItemType Directory -Path $full -Force | Out-Null
|
New-Item -ItemType Directory -Path $full -Force | Out-Null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Write-Host " 完成: $venvPy" -ForegroundColor Green
|
Write-Host " 完成: $venvPy" -ForegroundColor Green
|
||||||
} finally {
|
} finally {
|
||||||
Pop-Location
|
Pop-Location
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Setup-HubProject() {
|
function Setup-HubProject() {
|
||||||
$projPath = Join-Path $RepoRoot $HubProject.Dir
|
$projPath = Join-Path $RepoRoot $HubProject.Dir
|
||||||
if (-not (Test-Path $projPath)) {
|
if (-not (Test-Path $projPath)) {
|
||||||
Write-Host " 跳过 hub(目录不存在)" -ForegroundColor Yellow
|
Write-Host " 跳过 hub(目录不存在)" -ForegroundColor Yellow
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Write-Step $HubProject.Dir
|
Write-Step $HubProject.Dir
|
||||||
Push-Location $projPath
|
Push-Location $projPath
|
||||||
try {
|
try {
|
||||||
$venvDir = Join-Path $projPath ".venv"
|
$venvDir = Join-Path $projPath ".venv"
|
||||||
$venvPy = Join-Path $venvDir "Scripts\python.exe"
|
$venvPy = Join-Path $venvDir "Scripts\python.exe"
|
||||||
$venvPip = Join-Path $venvDir "Scripts\pip.exe"
|
$venvPip = Join-Path $venvDir "Scripts\pip.exe"
|
||||||
|
|
||||||
if ($RecreateVenv -and (Test-Path $venvDir)) {
|
if ($RecreateVenv -and (Test-Path $venvDir)) {
|
||||||
Remove-Item -Recurse -Force $venvDir
|
Remove-Item -Recurse -Force $venvDir
|
||||||
}
|
}
|
||||||
if (-not (Test-Path $venvPy)) {
|
if (-not (Test-Path $venvPy)) {
|
||||||
& python -m venv .venv
|
& python -m venv .venv
|
||||||
}
|
}
|
||||||
& $venvPy -m pip install -U pip setuptools wheel -q
|
& $venvPy -m pip install -U pip setuptools wheel -q
|
||||||
if (Test-Path $HubReqFile) {
|
if (Test-Path $HubReqFile) {
|
||||||
& $venvPip install -r $HubReqFile -q
|
& $venvPip install -r $HubReqFile -q
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-not $SkipEnvCopy) {
|
if (-not $SkipEnvCopy) {
|
||||||
$envExample = Join-Path $projPath ".env.example"
|
$envExample = Join-Path $projPath ".env.example"
|
||||||
$envFile = Join-Path $projPath ".env"
|
$envFile = Join-Path $projPath ".env"
|
||||||
if ((Test-Path $envExample) -and -not (Test-Path $envFile)) {
|
if ((Test-Path $envExample) -and -not (Test-Path $envFile)) {
|
||||||
Copy-Item $envExample $envFile
|
Copy-Item $envExample $envFile
|
||||||
Write-Host " 已复制 .env.example -> .env" -ForegroundColor Green
|
Write-Host " 已复制 .env.example -> .env" -ForegroundColor Green
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Write-Host " 完成: $venvPy" -ForegroundColor Green
|
Write-Host " 完成: $venvPy" -ForegroundColor Green
|
||||||
} finally {
|
} finally {
|
||||||
Pop-Location
|
Pop-Location
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Install-Pm2IfNeeded() {
|
function Install-Pm2IfNeeded() {
|
||||||
if ($SkipPm2) { return }
|
if ($SkipPm2) { return }
|
||||||
$node = Get-Command node -ErrorAction SilentlyContinue
|
$node = Get-Command node -ErrorAction SilentlyContinue
|
||||||
if (-not $node) {
|
if (-not $node) {
|
||||||
Write-Host "未检测到 Node.js,跳过 PM2。安装 Node 后执行: npm install -g pm2" -ForegroundColor Yellow
|
Write-Host "未检测到 Node.js,跳过 PM2。安装 Node 后执行: npm install -g pm2" -ForegroundColor Yellow
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Write-Step "PM2(可选进程托管)"
|
Write-Step "PM2(可选进程托管)"
|
||||||
$pm2 = Get-Command pm2 -ErrorAction SilentlyContinue
|
$pm2 = Get-Command pm2 -ErrorAction SilentlyContinue
|
||||||
if ($pm2) {
|
if ($pm2) {
|
||||||
Write-Host " PM2 已安装: $(pm2 -v)" -ForegroundColor Green
|
Write-Host " PM2 已安装: $(pm2 -v)" -ForegroundColor Green
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Write-Host " 正在全局安装 pm2 ..."
|
Write-Host " 正在全局安装 pm2 ..."
|
||||||
& npm install -g pm2
|
& npm install -g pm2
|
||||||
Write-Host " PM2 安装完成。在各子目录执行: pm2 start ecosystem.config.cjs" -ForegroundColor Green
|
Write-Host " PM2 安装完成。在各子目录执行: pm2 start ecosystem.config.cjs" -ForegroundColor Green
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- main ---
|
# --- main ---
|
||||||
Write-Host "crypto_monitor 环境部署" -ForegroundColor White
|
Write-Host "crypto_monitor 环境部署" -ForegroundColor White
|
||||||
Write-Host "仓库根目录: $RepoRoot" -ForegroundColor DarkGray
|
Write-Host "仓库根目录: $RepoRoot" -ForegroundColor DarkGray
|
||||||
|
|
||||||
if (-not (Test-Path $ReqFile)) {
|
if (-not (Test-Path $ReqFile)) {
|
||||||
throw "缺少 $ReqFile"
|
throw "缺少 $ReqFile"
|
||||||
}
|
}
|
||||||
|
|
||||||
Test-Python310
|
Test-Python310
|
||||||
|
|
||||||
$selected = ($Only -split "[,;\s]+" | ForEach-Object { $_.Trim().ToLower() } | Where-Object { $_ })
|
$selected = ($Only -split "[,;\s]+" | ForEach-Object { $_.Trim().ToLower() } | Where-Object { $_ })
|
||||||
if (-not $selected -or $selected.Count -eq 0) { $selected = @("all") }
|
if (-not $selected -or $selected.Count -eq 0) { $selected = @("all") }
|
||||||
|
|
||||||
foreach ($p in $MonitorProjects) {
|
foreach ($p in $MonitorProjects) {
|
||||||
if (Should-Include $p.Key $selected) {
|
if (Should-Include $p.Key $selected) {
|
||||||
Setup-MonitorProject $p
|
Setup-MonitorProject $p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Should-Include $HubProject.Key $selected) {
|
if (Should-Include $HubProject.Key $selected) {
|
||||||
Setup-HubProject
|
Setup-HubProject
|
||||||
}
|
}
|
||||||
|
|
||||||
Install-Pm2IfNeeded
|
Install-Pm2IfNeeded
|
||||||
|
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "部署完成。下一步:" -ForegroundColor Green
|
Write-Host "部署完成。下一步:" -ForegroundColor Green
|
||||||
Write-Host " 1. 编辑各子目录 .env(API Key、密码、代理等)"
|
Write-Host " 1. 编辑各子目录 .env(API Key、密码、代理等)"
|
||||||
Write-Host " 2. 启动示例(Binance):"
|
Write-Host " 2. 启动示例(Binance):"
|
||||||
Write-Host " cd crypto_monitor_binance"
|
Write-Host " cd crypto_monitor_binance"
|
||||||
Write-Host " .\.venv\Scripts\activate"
|
Write-Host " .\.venv\Scripts\activate"
|
||||||
Write-Host " python app.py"
|
Write-Host " python app.py"
|
||||||
Write-Host " 3. Linux 服务器可用: bash deploy/setup_env.sh"
|
Write-Host " 3. Linux 服务器可用: bash deploy/setup_env.sh"
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|||||||
@@ -75,6 +75,28 @@ def utc_window_to_bj_sql_strings(start_utc, end_utc, app_tz):
|
|||||||
return start_bj, end_bj
|
return start_bj, end_bj
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_bj_datetime_storage(raw):
|
||||||
|
"""表单 datetime-local(含 T)入库前统一为 YYYY-MM-DD HH:MM:SS(北京时间)。"""
|
||||||
|
s = (raw or "").strip().replace("T", " ").replace("Z", "").strip()
|
||||||
|
if not s:
|
||||||
|
return ""
|
||||||
|
for fmt, n in (("%Y-%m-%d %H:%M:%S", 19), ("%Y-%m-%d %H:%M", 16), ("%Y-%m-%d", 10)):
|
||||||
|
try:
|
||||||
|
return datetime.strptime(s[:n], fmt).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def sql_list_time_field(*columns):
|
||||||
|
"""
|
||||||
|
SQLite 列表时间窗比较表达式。
|
||||||
|
journal_entries 的 open/close 可能含 'T',直接与 bounds(空格格式)比会误判为超出上界。
|
||||||
|
"""
|
||||||
|
cols = ", ".join(columns)
|
||||||
|
return f"REPLACE(COALESCE({cols}), 'T', ' ')"
|
||||||
|
|
||||||
|
|
||||||
SESSION_KEY_LIST_WIN = "list_win_filter"
|
SESSION_KEY_LIST_WIN = "list_win_filter"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user