Initial commit with 2026 World Cup Quant Platform core modules and CI/CD

This commit is contained in:
QuantBot
2026-06-13 23:18:18 +08:00
commit 073abf98c1
155 changed files with 19539 additions and 0 deletions

View File

@@ -0,0 +1,116 @@
'use client';
import { useState } from 'react';
import { detectReverseLineMovement, type RlmResponse, type RlmRequestPayload } from '@/lib/analytics-api';
import { RLMRadarBoard } from '@/components/RLMRadarBoard';
export default function RlmPage() {
const [matchId, setMatchId] = useState('MATCH-RLM-2026-01');
const [marketType, setMarketType] = useState('1x2');
const [selection, setSelection] = useState('home');
const [ticketThreshold, setTicketThreshold] = useState(80);
const [oddsChangeThreshold, setOddsChangeThreshold] = useState(0.05);
const [loading, setLoading] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
const [result, setResult] = useState<RlmResponse | null>(null);
async function runRlm() {
setLoading(true);
setErrorMessage('');
try {
const payload: RlmRequestPayload = {
match_id: matchId,
market_type: marketType,
selection,
ticket_threshold: ticketThreshold,
odds_change_threshold: oddsChangeThreshold,
};
const data = await detectReverseLineMovement(payload);
setResult(data);
} catch (error) {
setErrorMessage(error instanceof Error ? error.message : 'RLM 偵測暫時無法使用');
setResult(null);
} finally {
setLoading(false);
}
}
return (
<div className="space-y-4">
<h2 className="dot-matrix text-2xl text-[#7d2a15]">RLM</h2>
<section className="panel-glow rounded-2xl p-4">
<h3 className="dot-matrix text-lg text-[#7d2a15]"></h3>
<div className="mt-3 grid gap-3 md:grid-cols-2 lg:grid-cols-4">
<label className="text-sm text-[#7a5b46]">
ID
<input
className="mt-2 w-full rounded-lg border border-[#e0c6a8] bg-white/70 px-3 py-2"
value={matchId}
onChange={(event) => setMatchId(event.target.value)}
/>
</label>
<label className="text-sm text-[#7a5b46]">
<input
className="mt-2 w-full rounded-lg border border-[#e0c6a8] bg-white/70 px-3 py-2"
value={marketType}
onChange={(event) => setMarketType(event.target.value)}
/>
</label>
<label className="text-sm text-[#7a5b46]">
<input
className="mt-2 w-full rounded-lg border border-[#e0c6a8] bg-white/70 px-3 py-2"
value={selection}
onChange={(event) => setSelection(event.target.value)}
/>
</label>
<label className="text-sm text-[#7a5b46]">
%
<input
className="mt-2 w-full rounded-lg border border-[#e0c6a8] bg-white/70 px-3 py-2"
type="number"
value={ticketThreshold}
onChange={(event) => setTicketThreshold(Number(event.target.value))}
/>
</label>
<label className="text-sm text-[#7a5b46]">
<input
className="mt-2 w-full rounded-lg border border-[#e0c6a8] bg-white/70 px-3 py-2"
type="number"
step={0.01}
value={oddsChangeThreshold}
onChange={(event) => setOddsChangeThreshold(Number(event.target.value))}
/>
</label>
</div>
<div className="mt-4">
<button
className="rounded-lg bg-[#7d2a15] px-4 py-2 text-white"
type="button"
onClick={runRlm}
disabled={loading}
>
{loading ? '偵測中…' : '執行 RLM 偵測'}
</button>
</div>
{errorMessage ? <p className="mt-3 text-xs text-[#8c2f2f]">{errorMessage}</p> : null}
</section>
<RLMRadarBoard
matchId={matchId}
alerts={result?.alerts || []}
/>
<section className="panel-glow rounded-2xl p-4">
<p className="text-sm text-[#7a5b46]">
{result?.total ?? 0}
{' '}
| {result?.alerts ? result.alerts.filter((item) => item.is_triggered).length : 0}
</p>
</section>
</div>
);
}