cyberhybridhub/server/lib/market_history_env.dart
2026-05-31 13:11:09 -05:00

154 lines
4.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// Market-history pipeline settings loaded from env (§7).
class MarketHistoryEnv {
MarketHistoryEnv({
required this.syncEnabled,
required this.windowDays,
required this.retentionDays,
required this.archiveEnabled,
required this.universeRefreshHours,
required this.historySyncHours,
required this.cleanupHours,
required this.syncHourUtc,
required this.historySyncBatchSize,
required this.historySyncMaxSymbols,
required this.minBarsForGuess,
required this.guessCooldownHours,
required this.apiRequestsPerMinute,
required this.staleSyncRunMinutes,
});
final bool syncEnabled;
final int windowDays;
final int retentionDays;
final bool archiveEnabled;
final int universeRefreshHours;
final int historySyncHours;
final int cleanupHours;
/// UTC hour (023) for optional daily alignment, or `null` when unset.
final int? syncHourUtc;
final int historySyncBatchSize;
final int historySyncMaxSymbols;
final int minBarsForGuess;
final int guessCooldownHours;
final int apiRequestsPerMinute;
final int staleSyncRunMinutes;
static MarketHistoryEnv fromMap(Map<String, String> env) {
final bool syncEnabled =
(env['MARKET_HISTORY_SYNC_ENABLED'] ?? 'false').toLowerCase() == 'true';
final int windowDays =
_positiveInt(env['MARKET_HISTORY_WINDOW_DAYS'], defaultValue: 5, name: 'MARKET_HISTORY_WINDOW_DAYS');
final int retentionDays = _positiveInt(
env['MARKET_HISTORY_RETENTION_DAYS'],
defaultValue: 5,
name: 'MARKET_HISTORY_RETENTION_DAYS',
);
final bool archiveEnabled =
(env['MARKET_HISTORY_ARCHIVE_ENABLED'] ?? 'false').toLowerCase() ==
'true';
final int universeRefreshHours = _positiveInt(
env['MARKET_UNIVERSE_REFRESH_HOURS'],
defaultValue: 24,
name: 'MARKET_UNIVERSE_REFRESH_HOURS',
);
final int historySyncHours = _positiveInt(
env['MARKET_HISTORY_SYNC_HOURS'],
defaultValue: 24,
name: 'MARKET_HISTORY_SYNC_HOURS',
);
final int cleanupHours = _positiveInt(
env['MARKET_HISTORY_CLEANUP_HOURS'],
defaultValue: 24,
name: 'MARKET_HISTORY_CLEANUP_HOURS',
);
final int? syncHourUtc = _optionalSyncHourUtc(env['MARKET_HISTORY_SYNC_HOUR_UTC']);
final int historySyncBatchSize = _positiveInt(
env['HISTORY_SYNC_BATCH_SIZE'],
defaultValue: 50,
name: 'HISTORY_SYNC_BATCH_SIZE',
);
final int historySyncMaxSymbols = _positiveInt(
env['HISTORY_SYNC_MAX_SYMBOLS'],
defaultValue: 2000,
name: 'HISTORY_SYNC_MAX_SYMBOLS',
);
final int minBarsForGuess = _positiveInt(
env['MIN_BARS_FOR_GUESS'],
defaultValue: 5,
name: 'MIN_BARS_FOR_GUESS',
);
final int guessCooldownHours = _positiveInt(
env['GUESS_COOLDOWN_HOURS'],
defaultValue: 24,
name: 'GUESS_COOLDOWN_HOURS',
);
final int apiRequestsPerMinute = _positiveInt(
env['MARKET_HISTORY_API_REQUESTS_PER_MINUTE'],
defaultValue: 200,
name: 'MARKET_HISTORY_API_REQUESTS_PER_MINUTE',
);
final int staleSyncRunMinutes = _positiveInt(
env['MARKET_HISTORY_SYNC_STALE_MINUTES'],
defaultValue: 30,
name: 'MARKET_HISTORY_SYNC_STALE_MINUTES',
);
return MarketHistoryEnv(
syncEnabled: syncEnabled,
windowDays: windowDays,
retentionDays: retentionDays,
archiveEnabled: archiveEnabled,
universeRefreshHours: universeRefreshHours,
historySyncHours: historySyncHours,
cleanupHours: cleanupHours,
syncHourUtc: syncHourUtc,
historySyncBatchSize: historySyncBatchSize,
historySyncMaxSymbols: historySyncMaxSymbols,
minBarsForGuess: minBarsForGuess,
guessCooldownHours: guessCooldownHours,
apiRequestsPerMinute: apiRequestsPerMinute,
staleSyncRunMinutes: staleSyncRunMinutes,
);
}
/// Fails fast when market-history flags conflict with trading gates.
void assertConsistent({required bool tradingEnabled}) {
if (syncEnabled && !tradingEnabled) {
throw StateError(
'MARKET_HISTORY_SYNC_ENABLED=true requires TRADING_ENABLED=true',
);
}
}
static int _positiveInt(
String? raw, {
required int defaultValue,
required String name,
}) {
if (raw == null || raw.trim().isEmpty) {
return defaultValue;
}
final int? parsed = int.tryParse(raw.trim());
if (parsed == null || parsed <= 0) {
throw ArgumentError.value(raw, name, 'must be a positive integer');
}
return parsed;
}
static int? _optionalSyncHourUtc(String? raw) {
if (raw == null || raw.trim().isEmpty) {
return null;
}
final int? parsed = int.tryParse(raw.trim());
if (parsed == null || parsed < 0 || parsed > 23) {
throw ArgumentError.value(
raw,
'MARKET_HISTORY_SYNC_HOUR_UTC',
'must be an integer from 0 to 23',
);
}
return parsed;
}
}