Retry incomplete SET bars and rebuild stale MT5 signals

This commit is contained in:
PrimeThai Automation
2026-10-29 17:04:21 +07:00
parent bb301f1ca6
commit 96ea8a37ce
3 changed files with 32 additions and 10 deletions

View File

@@ -342,3 +342,9 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\mt5_app\install-autost
- ไฟล์ `data/supervisor.stop` เป็นเครื่องหมายหยุดที่คงอยู่ข้ามรอบ Task จึงไม่ถูกล้างเองทุกครั้งที่ตัวดูแลเริ่ม ติดตั้ง/เปิดผ่าน `install-autostart.ps1` จะล้างเครื่องหมายโดยตั้งใจ ส่วน `-Remove` ปิด trigger ก่อนหยุดและถอด Task
ยังไม่ทราบว่าโปรแกรมใดเปิด/ล็อกไฟล์ในจังหวะนั้น การแก้ไม่เปลี่ยนสิทธิ์ไฟล์หรือใช้ Administrator และไม่ล้างฐานข้อมูลบัญชี/คำสั่งเดิม
### ตรวจความครบของข้อมูล SET หลังเปิดแอปกลับ
ระหว่างกู้แอปวันที่ 29 ต.ค. พบว่าเว็บและ MT5 ยังเชื่อมต่อได้ แต่สัญญาณคำนวณต่อไม่ได้เพราะ SET ใน SQLite ถึง 27 ต.ค. ขณะที่หุ้นถึง 28 ต.ค. แม้ MT5 มี SET วันที่ 28 แล้ว สาเหตุคือแอปจำวันที่ร้องขอเป็นวันที่โหลดสำเร็จ แล้วข้าม SET ในรอบถัดไป
แก้ให้ตรวจวันที่แท่ง SET ที่เก็บจริงก่อนใช้ cache หากยังไม่ถึงวันเป้าหมายจะดึง SET ใหม่และเก็บ latest_day ถ้าแหล่งข้อมูลยังไม่ครบจะลองใหม่ตามรอบข้อมูลเดิม หากสัญญาณเก่ากว่าข้อมูลที่ต้องใช้จะคำนวณใหม่ และ dashboard จะขึ้นรอข้อมูลแทนแผนเก่า โดยไม่เปลี่ยนกฎ Donchian/EMA หรือเติมดัชนีที่ไม่มีข้อมูลจริงเอง

View File

@@ -43,7 +43,8 @@ def plain(value):
def download_history(broker, store, settings, progress, *, force=False):
through = settings.closed_through()
if store.get('history_checked_through') == through and not force:
benchmark_ready = store.last_bar_day('SET', through) == through
if store.get('history_checked_through') == through and benchmark_ready and not force:
progress('history', 'ใช้ประวัติ MT5 ที่เก็บไว้แล้ว', 1, 1)
return store.get('history_download', {})
symbols = [s.name for s in broker.symbols()
@@ -57,8 +58,11 @@ def download_history(broker, store, settings, progress, *, force=False):
progress('history', f'อ่าน D1 {symbol} · {number}/{len(symbols)}', number, len(symbols))
try:
symbol_state = store.get('history_symbol:' + symbol, {})
if (symbol_state.get('through') == through
or (symbol in previous and previous_record.get('through') == through)) and not force:
cached = (symbol_state.get('through') == through
or (symbol in previous and previous_record.get('through') == through))
# A successful download request is not proof the benchmark reached
# the requested session. Retry SET until the actual D1 bar arrives.
if cached and not force and (symbol != 'SET' or benchmark_ready):
successful.append(symbol)
if symbol in previous_record.get('zero_real_volume', []):
zero_volume.append(symbol)
@@ -69,8 +73,8 @@ def download_history(broker, store, settings, progress, *, force=False):
rates = broker.history(symbol, count)
rows = []
for r in rates:
# D1 timestamps label the exchange session. MT5 stores them as
# UTC; do not use the workstation timezone to shift the date.
# D1 calendar dates label broker sessions; keep that raw date.
# Intraday quote/server-clock conversion must not shift D1 labels.
day = datetime.fromtimestamp(int(r['time']), timezone.utc).date().isoformat()
prices = [float(r[c]) for c in ('open', 'high', 'low', 'close')]
if day < settings.warmup_start or day > through:
@@ -83,7 +87,11 @@ def download_history(broker, store, settings, progress, *, force=False):
if symbol != 'SET' and all(r[6] <= 0 for r in rows):
zero_volume.append(symbol)
store.save_bars(rows)
store.put('history_symbol:' + symbol, {'through': through, 'rows': len(rows), 'checked_at': now_iso()})
latest_day = max(r[1] for r in rows)
store.put('history_symbol:' + symbol, {'through': through, 'latest_day': latest_day,
'rows': len(rows), 'checked_at': now_iso()})
if symbol == 'SET' and latest_day != through:
raise ValueError(f'SET D1 ล่าสุด {latest_day}; ต้องถึง {through} จะลองดึงใหม่ในรอบถัดไป')
successful.append(symbol)
except Exception as exc:
failures.append({'symbol': symbol, 'error': str(exc)})

View File

@@ -247,16 +247,23 @@ class Desk:
last_attempt = previous.get('checked_at')
retry_due = (not last_attempt or time.time() - datetime.fromisoformat(last_attempt).timestamp() >= 600)
needs_new_day = previous.get('through') != self.settings.closed_through()
incomplete = self.store.get('history_checked_through') != self.settings.closed_through()
if research or not model or needs_new_day or (incomplete and retry_due):
incomplete = (self.store.get('history_checked_through') != self.settings.closed_through()
or self.store.last_bar_day('SET', self.settings.closed_through())
!= self.settings.closed_through())
model_due = model.get('signal_as_of') != self.settings.closed_through()
if research or not model or needs_new_day or ((incomplete or model_due) and retry_due):
before = self.store.bar_summary()
download_history(self.broker, self.store, self.settings, self.progress)
if research or not model or needs_new_day or self.store.bar_summary() != before:
if research or not model or needs_new_day or model_due or self.store.bar_summary() != before:
model = run_research(self.store, self.settings, self.progress)
snapshot = self.broker.snapshot()
self.progress('quotes', 'อ่านราคาเสนอซื้อขายของหุ้นในแผน')
self.update_quotes(snapshot, model)
self.store.put('job', {'running': False, 'stage': 'complete', 'message': 'พร้อมใช้งาน', 'updated_at': now_iso()})
pending = model.get('signal_as_of') != self.settings.closed_through()
message = (f'พอร์ตอัปเดตแล้ว แต่สัญญาณถึง {model.get("signal_as_of", "ยังไม่มี")} '
f'รอข้อมูล/คำนวณถึง {self.settings.closed_through()}' if pending else 'พร้อมใช้งาน')
self.store.put('job', {'running': False, 'stage': 'waiting_data' if pending else 'complete',
'message': message, 'updated_at': now_iso()})
except Exception as exc:
self.log.exception('refresh_failed')
self.store.put('connection_error', {'message': str(exc), 'at': now_iso()})
@@ -274,6 +281,7 @@ class Desk:
job = self.store.get('job', {})
plan = make_plan(snap, model, self.store.skipped(day), self.settings)
if (not model.get('data_quality', {}).get('calendar_source')
or model.get('signal_as_of') != self.settings.closed_through()
or (job.get('running') and job.get('stage') in ('history', 'archive', 'signals', 'comparison'))):
for row in plan['rows']:
row.update(action='WAIT', quantity=0, reason='รอการตรวจปฏิทินข้อมูล MT5 ให้ครบ')