移除全部合约同步按钮,改为单合约后台同步带进度条
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+79
-71
@@ -5,119 +5,127 @@ from app.database import SessionLocal
|
||||
from app.models import DailyBar, Contract
|
||||
from app.engine.lock_strategy import compute_amp_5d
|
||||
|
||||
_sync_progress = {
|
||||
"running": False,
|
||||
"contract": "",
|
||||
"contract_done": 0,
|
||||
"contract_total": 0,
|
||||
"date_done": 0,
|
||||
"date_total": 0,
|
||||
"rows": 0,
|
||||
"finished": False,
|
||||
}
|
||||
_progress = {"running": False, "label": "", "done": 0, "total": 0, "finished": False}
|
||||
_lock = threading.Lock()
|
||||
|
||||
|
||||
def get_sync_progress() -> dict:
|
||||
def get_progress() -> dict:
|
||||
with _lock:
|
||||
return dict(_sync_progress)
|
||||
return dict(_progress)
|
||||
|
||||
|
||||
def start_sync_positions_background():
|
||||
"""Start sync_position_rankings in a background thread. Returns immediately."""
|
||||
def _start_bg(target, label):
|
||||
with _lock:
|
||||
if _sync_progress["running"]:
|
||||
if _progress["running"]:
|
||||
return False
|
||||
_sync_progress.update({
|
||||
"running": True,
|
||||
"finished": False,
|
||||
"contract": "",
|
||||
"contract_done": 0,
|
||||
"contract_total": 0,
|
||||
"date_done": 0,
|
||||
"date_total": 0,
|
||||
"rows": 0,
|
||||
})
|
||||
_progress.update(running=True, finished=False, label=label, done=0, total=0)
|
||||
|
||||
def _run():
|
||||
try:
|
||||
_do_sync_with_progress()
|
||||
target()
|
||||
finally:
|
||||
with _lock:
|
||||
_sync_progress["running"] = False
|
||||
_sync_progress["finished"] = True
|
||||
_progress["running"] = False
|
||||
_progress["finished"] = True
|
||||
_progress["done"] = _progress["total"]
|
||||
|
||||
threading.Thread(target=_run, daemon=True).start()
|
||||
return True
|
||||
|
||||
|
||||
def _do_sync_with_progress():
|
||||
from app.models import PositionRanking
|
||||
def sync_contract_bars_bg(contract_code: str) -> bool:
|
||||
"""Sync bars for one contract in background. Returns True if started."""
|
||||
code = contract_code.upper()
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
active_contracts = (
|
||||
db.query(Contract).filter(Contract.is_active == True).all()
|
||||
)
|
||||
with _lock:
|
||||
_sync_progress["contract_total"] = len(active_contracts)
|
||||
def _run():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
latest = (
|
||||
db.query(DailyBar.date)
|
||||
.filter(DailyBar.contract == code)
|
||||
.order_by(DailyBar.date.desc())
|
||||
.first()
|
||||
)
|
||||
start_date = latest[0].isoformat() if latest else None
|
||||
bars = fetch_contract_bars(code, start_date)
|
||||
|
||||
for idx, c in enumerate(active_contracts):
|
||||
with _lock:
|
||||
_sync_progress.update({
|
||||
"contract": c.code,
|
||||
"contract_done": idx,
|
||||
"date_done": 0,
|
||||
"date_total": 0,
|
||||
})
|
||||
_progress["total"] = len(bars)
|
||||
|
||||
code = c.code.upper()
|
||||
inserted = 0
|
||||
min_date = None
|
||||
for i, bar in enumerate(bars):
|
||||
existing = (
|
||||
db.query(DailyBar)
|
||||
.filter(DailyBar.contract == code, DailyBar.date == bar["date"])
|
||||
.first()
|
||||
)
|
||||
if not existing:
|
||||
db.add(DailyBar(
|
||||
contract=code, date=bar["date"],
|
||||
open=bar["open"], close=bar["close"],
|
||||
high=bar["high"], low=bar["low"],
|
||||
))
|
||||
inserted += 1
|
||||
if min_date is None or bar["date"] < min_date:
|
||||
min_date = bar["date"]
|
||||
with _lock:
|
||||
_progress["done"] = i + 1
|
||||
|
||||
if inserted > 0:
|
||||
db.flush()
|
||||
_recompute_amp(db, code, from_date=min_date)
|
||||
db.commit()
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
return _start_bg(_run, f"同步行情 {code}")
|
||||
|
||||
|
||||
def sync_positions_bg(contract_code: str) -> bool:
|
||||
"""Sync position rankings for one contract in background. Returns True if started."""
|
||||
from app.models import PositionRanking
|
||||
code = contract_code.upper()
|
||||
|
||||
def _run():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
existing_dates = {
|
||||
r[0] for r in
|
||||
db.query(PositionRanking.date)
|
||||
.filter(PositionRanking.contract_code == code)
|
||||
.distinct()
|
||||
.all()
|
||||
.distinct().all()
|
||||
}
|
||||
|
||||
bar_dates = [
|
||||
r[0] for r in
|
||||
db.query(DailyBar.date)
|
||||
.filter(DailyBar.contract == code)
|
||||
.order_by(DailyBar.date)
|
||||
.all()
|
||||
.order_by(DailyBar.date).all()
|
||||
]
|
||||
|
||||
missing = [d for d in bar_dates if d not in existing_dates]
|
||||
with _lock:
|
||||
_sync_progress["date_total"] = len(missing)
|
||||
|
||||
for date_idx, d in enumerate(missing):
|
||||
date_str = d.strftime("%Y%m%d")
|
||||
rankings = fetch_position_rankings(code, date_str)
|
||||
inserted = 0
|
||||
with _lock:
|
||||
_progress["total"] = len(missing)
|
||||
|
||||
inserted = 0
|
||||
for i, d in enumerate(missing):
|
||||
rankings = fetch_position_rankings(code, d.strftime("%Y%m%d"))
|
||||
for r in rankings:
|
||||
db.add(PositionRanking(
|
||||
contract_code=code,
|
||||
institution=r["institution"],
|
||||
data_type=r["data_type"],
|
||||
date=d,
|
||||
rank=r["rank"],
|
||||
value=r["value"],
|
||||
change=r["change"],
|
||||
contract_code=code, institution=r["institution"],
|
||||
data_type=r["data_type"], date=d,
|
||||
rank=r["rank"], value=r["value"], change=r["change"],
|
||||
))
|
||||
inserted += 1
|
||||
db.flush()
|
||||
with _lock:
|
||||
_sync_progress["date_done"] = date_idx + 1
|
||||
_sync_progress["rows"] += inserted
|
||||
_progress["done"] = i + 1
|
||||
|
||||
db.commit()
|
||||
with _lock:
|
||||
_sync_progress["contract_done"] = len(active_contracts)
|
||||
finally:
|
||||
db.close()
|
||||
db.commit()
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
return _start_bg(_run, f"同步持仓 {code}")
|
||||
|
||||
|
||||
def fetch_contract_bars(contract_code: str, start_date: str | None = None) -> list[dict]:
|
||||
|
||||
Reference in New Issue
Block a user