增加新账户

This commit is contained in:
dekun
2026-05-12 18:27:35 +08:00
parent 02a4bb10ab
commit 5900588718
34 changed files with 17309 additions and 3 deletions
@@ -0,0 +1,108 @@
#!/usr/bin/env python3
"""
一次性修复历史交易记录标签:
将 trade_records 里“止损但实际盈利”的记录改为“保本止盈”。
默认条件(可通过参数修改):
- monitor_type = 下单监控
- result = 止损
- pnl_amount > 0
用法示例:
1) 仅预览(不落库):
python scripts/fix_breakeven_labels.py --db ./crypto.db --dry-run
2) 执行修复:
python scripts/fix_breakeven_labels.py --db ./crypto.db --apply
"""
from __future__ import annotations
import argparse
import sqlite3
import sys
from pathlib import Path
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Fix historical stop-loss records with positive pnl.")
parser.add_argument("--db", required=True, help="Path to sqlite db file, e.g. ./crypto.db")
parser.add_argument("--monitor-type", default="下单监控", help="Filter by monitor_type (default: 下单监控)")
parser.add_argument("--from-result", default="止损", help="Source result label (default: 止损)")
parser.add_argument("--to-result", default="保本止盈", help="Target result label (default: 保本止盈)")
parser.add_argument("--dry-run", action="store_true", help="Preview only, no write")
parser.add_argument("--apply", action="store_true", help="Execute update")
return parser.parse_args()
def main() -> int:
args = parse_args()
db_path = Path(args.db).expanduser().resolve()
if not db_path.exists():
print(f"[ERR] DB not found: {db_path}")
return 1
if args.dry_run and args.apply:
print("[ERR] --dry-run and --apply are mutually exclusive.")
return 1
if not args.dry_run and not args.apply:
print("[INFO] No mode provided, defaulting to --dry-run.")
args.dry_run = True
conn = sqlite3.connect(str(db_path))
conn.row_factory = sqlite3.Row
cur = conn.cursor()
where_sql = """
monitor_type = ?
AND result = ?
AND CAST(COALESCE(pnl_amount, 0) AS REAL) > 0
"""
params = (args.monitor_type, args.from_result)
cur.execute(f"SELECT COUNT(*) AS c FROM trade_records WHERE {where_sql}", params)
will_change = int(cur.fetchone()["c"])
print(f"[INFO] Candidate rows: {will_change}")
if will_change == 0:
print("[INFO] Nothing to update.")
conn.close()
return 0
cur.execute(
f"""
SELECT id, symbol, result, pnl_amount, closed_at
FROM trade_records
WHERE {where_sql}
ORDER BY id DESC
LIMIT 10
""",
params,
)
sample = cur.fetchall()
print("[INFO] Sample (latest 10):")
for r in sample:
print(
f" id={r['id']} symbol={r['symbol']} result={r['result']} "
f"pnl={r['pnl_amount']} closed_at={r['closed_at']}"
)
if args.dry_run:
print("[DRY-RUN] No write executed.")
conn.close()
return 0
cur.execute(
f"UPDATE trade_records SET result=? WHERE {where_sql}",
(args.to_result, *params),
)
changed = int(cur.rowcount)
conn.commit()
conn.close()
print(f"[DONE] Updated rows: {changed}")
return 0
if __name__ == "__main__":
sys.exit(main())
@@ -0,0 +1,93 @@
"""
在项目根目录执行(会加载根目录 .env):
python scripts/verify_gate_funding.py
依次探测:[0] swap 余额(与 App「交易账户」同源);[1]–[3] 现货 / 统一账户资金路径。
打印 GATE_API_KEY 前 8 位便于与 Gate 控制台核对(不含 Secret)。用于服务器自检。
"""
from __future__ import annotations
import importlib.util
import os
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if ROOT not in sys.path:
sys.path.insert(0, ROOT)
def _load_app():
path = os.path.join(ROOT, "app.py")
spec = importlib.util.spec_from_file_location("crypto_app", path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
def main():
os.chdir(ROOT)
mod = _load_app()
print("LIVE_TRADING_ENABLED =", os.getenv("LIVE_TRADING_ENABLED"))
ok, reason = mod.ensure_exchange_live_ready()
print("ensure_exchange_live_ready =", ok, repr(reason))
if not ok:
print("跳过私有接口探测")
return 1
mod.ensure_markets_loaded()
k = (os.getenv("GATE_API_KEY") or "").strip()
s = (os.getenv("GATE_API_SECRET") or "").strip()
if not k or "REPLACE" in k.upper():
print("WARN: GATE_API_KEY 为空或仍像占位符,请核对 .env")
if not s or "REPLACE" in s.upper():
print("WARN: GATE_API_SECRET 为空或仍像占位符,请核对 .env")
print("GATE_API_KEY prefix (8 chars):", (k[:8] + "") if len(k) > 8 else "(short)")
# 0) swap — 与 App「交易账户」余额同源(优先看此项是否与网页一致)
try:
bal = mod.exchange.fetch_balance({"type": "swap"})
v0 = mod._extract_usdt_total(bal)
print("[0] fetch_balance(swap) USDT total =", v0)
except Exception as e:
print("[0] fetch_balance(swap) FAILED:", type(e).__name__, e)
# 1) fetch_balance spot + marginMode spot
try:
bal = mod.exchange.fetch_balance({"type": "spot", "marginMode": "spot"})
v = mod._extract_usdt_total(bal)
print("[1] fetch_balance(spot,marginMode=spot) USDT total =", v)
except Exception as e:
print("[1] fetch_balance(spot) FAILED:", type(e).__name__, e)
# 2) raw spot accounts
try:
resp = mod.exchange.privateSpotGetAccounts({})
v2 = mod._parse_gate_spot_accounts_response_usdt(resp)
print("[2] privateSpotGetAccounts USDT =", v2)
except Exception as e:
print("[2] privateSpotGetAccounts FAILED:", type(e).__name__, e)
# 3) unified accounts raw
try:
raw = mod.exchange.privateUnifiedGetAccounts({})
body = raw
if isinstance(body, dict) and isinstance(body.get("result"), dict):
body = body["result"]
if isinstance(body, dict):
keys = sorted(body.keys())
print("[3] unified top-level keys (sample):", keys[:25], "..." if len(keys) > 25 else "")
v3 = mod._parse_usdt_from_gate_unified_accounts_body(body) if isinstance(body, dict) else None
print("[3] parsed unified USDT =", v3)
except Exception as e:
print("[3] privateUnifiedGetAccounts FAILED:", type(e).__name__, e)
fu = mod._fetch_gate_funding_usdt()
print(">>> _fetch_gate_funding_usdt() =", fu)
f, t = mod.get_exchange_capitals(force=True)
print(">>> get_exchange_capitals(force=True) funding, trading =", f, t)
return 0
if __name__ == "__main__":
raise SystemExit(main())