154 lines
4.8 KiB
Dart
154 lines
4.8 KiB
Dart
/// 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 (0–23) 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;
|
||
}
|
||
}
|