feat: near-close scan schedule and distinct-day gate reset
Move the only qualifying R:R scan to 15:30 ET with chained Telegram alerts, put outcome eval after a final-bar OHLCV fetch, enforce NY trading-day requalify semantics, stamp paper trades fill_mode=near_close, and migrate stored schedule_* keys to America/New_York.
This commit is contained in:
@@ -4,34 +4,48 @@ import { useScheduleSettings, useUpdateScheduleSettings } from '../../hooks/useA
|
||||
import { SkeletonTable } from '../ui/Skeleton';
|
||||
|
||||
const DEFAULTS: ScheduleConfig = {
|
||||
schedule_timezone: 'Europe/Berlin',
|
||||
schedule_daily_pipeline_cron: '0 7 * * *',
|
||||
schedule_intraday_pipeline_cron: '0 14-22 * * 1-5',
|
||||
schedule_fundamentals_cron: '0 4 * * 1',
|
||||
schedule_timezone: 'America/New_York',
|
||||
schedule_daily_pipeline_cron: '0 2 * * *',
|
||||
schedule_near_close_pipeline_cron: '30 15 * * 1-5',
|
||||
schedule_after_close_pipeline_cron: '45 16 * * 1-5',
|
||||
schedule_intraday_pipeline_cron: '0 10-15 * * 1-5',
|
||||
schedule_fundamentals_cron: '0 1 * * 1',
|
||||
};
|
||||
|
||||
const FIELDS: { key: keyof ScheduleConfig; label: string; hint: string; mono?: boolean }[] = [
|
||||
{
|
||||
key: 'schedule_timezone',
|
||||
label: 'Timezone',
|
||||
hint: 'IANA name, e.g. Europe/Berlin. All times below are in this zone.',
|
||||
hint: 'IANA name. Prefer America/New_York so the near-close scan tracks the US cash close through DST.',
|
||||
},
|
||||
{
|
||||
key: 'schedule_daily_pipeline_cron',
|
||||
label: 'Daily pipeline (full)',
|
||||
hint: 'OHLCV → sentiment → R:R scan → outcomes → regime. Default 07:00 so data is ready by 8.',
|
||||
label: 'Morning pipeline',
|
||||
hint: 'OHLCV → benchmark → sentiment → regime (no R:R scan). Default 02:00 ET.',
|
||||
mono: true,
|
||||
},
|
||||
{
|
||||
key: 'schedule_near_close_pipeline_cron',
|
||||
label: 'Near-close pipeline (scan + alert)',
|
||||
hint: 'OHLCV fetch → R:R scan → Telegram. Default 15:30 ET Mon–Fri so manual MOC fills can still hit ~15:50/15:55.',
|
||||
mono: true,
|
||||
},
|
||||
{
|
||||
key: 'schedule_after_close_pipeline_cron',
|
||||
label: 'After-close pipeline (outcome)',
|
||||
hint: 'OHLCV fetch (final bar) → outcome eval. Default 16:45 ET Mon–Fri — not chained to the partial near-close bar.',
|
||||
mono: true,
|
||||
},
|
||||
{
|
||||
key: 'schedule_intraday_pipeline_cron',
|
||||
label: 'Intraday pipeline (light)',
|
||||
hint: 'Refresh prices + resolve outcomes. Default hourly across the US session, weekdays.',
|
||||
hint: 'Refresh prices + resolve outcomes mid-session. Default hourly 10:00–15:00 ET weekdays.',
|
||||
mono: true,
|
||||
},
|
||||
{
|
||||
key: 'schedule_fundamentals_cron',
|
||||
label: 'Fundamentals (weekly)',
|
||||
hint: 'Slow, rate-limited. Default early Monday so it finishes well before the day starts.',
|
||||
hint: 'Slow, rate-limited. Default early Monday ET.',
|
||||
mono: true,
|
||||
},
|
||||
];
|
||||
@@ -43,7 +57,7 @@ export function ScheduleSettings() {
|
||||
const [form, setForm] = useState<ScheduleConfig>(DEFAULTS);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) setForm(data);
|
||||
if (data) setForm({ ...DEFAULTS, ...data });
|
||||
}, [data]);
|
||||
|
||||
if (isLoading) return <SkeletonTable rows={2} cols={2} />;
|
||||
@@ -55,8 +69,8 @@ export function ScheduleSettings() {
|
||||
<h3 className="text-sm font-semibold text-gray-200">Pipeline Schedule</h3>
|
||||
<p className="mt-1 text-xs text-gray-500">
|
||||
When the jobs run, as 5-field cron (<span className="num">min hour day month weekday</span>).
|
||||
Saved changes apply to the running scheduler immediately — no redeploy. The big nightly run
|
||||
does the full refresh; the light intraday run just keeps prices current.
|
||||
Saved changes apply to the running scheduler immediately — no redeploy.
|
||||
One qualifying R:R scan per day is the near-close job; alerts fire immediately after that scan.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -66,7 +80,7 @@ export function ScheduleSettings() {
|
||||
<span className="text-xs text-gray-400">{f.label}</span>
|
||||
<input
|
||||
type="text"
|
||||
value={form[f.key]}
|
||||
value={form[f.key] ?? ''}
|
||||
spellCheck={false}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, [f.key]: e.target.value }))}
|
||||
className={`w-full input-glass px-3 py-2 text-sm ${f.mono ? 'num' : ''}`}
|
||||
|
||||
@@ -187,10 +187,12 @@ export interface ActivationConfig {
|
||||
exclude_neutral: boolean;
|
||||
}
|
||||
|
||||
// Cron schedule for the daily/intraday pipelines + fundamentals
|
||||
// Cron schedule for morning / near-close / after-close / intraday + fundamentals
|
||||
export interface ScheduleConfig {
|
||||
schedule_timezone: string;
|
||||
schedule_daily_pipeline_cron: string;
|
||||
schedule_near_close_pipeline_cron: string;
|
||||
schedule_after_close_pipeline_cron: string;
|
||||
schedule_intraday_pipeline_cron: string;
|
||||
schedule_fundamentals_cron: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user