class QuestionAuditBarSlot { const QuestionAuditBarSlot({ required this.asOf, required this.avgPrice, required this.volume, required this.volumeUsd, this.open, this.high, this.low, this.close, }); final DateTime asOf; final num? open; final num? high; final num? low; final num? close; final num avgPrice; final num volume; final num volumeUsd; factory QuestionAuditBarSlot.fromJson(Map json) { final num avgPrice = json['avgPrice'] as num; final num volume = json['volume'] as num; return QuestionAuditBarSlot( asOf: DateTime.parse(json['asOf']! as String).toUtc(), open: json['open'] as num?, high: json['high'] as num?, low: json['low'] as num?, close: json['close'] as num?, avgPrice: avgPrice, volume: volume, volumeUsd: json['volumeUsd'] as num? ?? volume * avgPrice, ); } } class QuestionAuditAsset { const QuestionAuditAsset({ required this.symbol, required this.priceDelta, required this.volumeDelta, required this.olderSlot, required this.newerSlot, }); final String symbol; final num priceDelta; final num volumeDelta; final QuestionAuditBarSlot olderSlot; final QuestionAuditBarSlot newerSlot; factory QuestionAuditAsset.fromJson(Map json) { return QuestionAuditAsset( symbol: json['symbol']! as String, priceDelta: json['priceDelta'] as num, volumeDelta: json['volumeDelta'] as num, olderSlot: QuestionAuditBarSlot.fromJson( json['olderSlot']! as Map, ), newerSlot: QuestionAuditBarSlot.fromJson( json['newerSlot']! as Map, ), ); } } /// US RTH session-half slot length (9:30–12:45 and 12:45–16:00 ET). const Duration _sessionHalfSlotDuration = Duration(hours: 3, minutes: 15); class QuestionAuditReport { const QuestionAuditReport({ required this.compareUntil, required this.newerSlotStart, required this.olderSlotStart, required this.windowDays, required this.assets, required this.canStepOlder, required this.canStepNewer, this.stepOlderCompareUntil, this.stepNewerCompareUntil, }); final DateTime compareUntil; final DateTime newerSlotStart; final DateTime olderSlotStart; final int windowDays; final List assets; final bool canStepOlder; final bool canStepNewer; final DateTime? stepOlderCompareUntil; final DateTime? stepNewerCompareUntil; factory QuestionAuditReport.fromJson(Map json) { final List raw = json['assets'] as List? ?? []; final DateTime compareUntil = DateTime.parse( (json['compareUntil'] ?? json['asOf'])! as String, ).toUtc(); return QuestionAuditReport( compareUntil: compareUntil, newerSlotStart: json['newerSlotStart'] == null ? compareUntil.subtract(_sessionHalfSlotDuration) : DateTime.parse(json['newerSlotStart']! as String).toUtc(), olderSlotStart: json['olderSlotStart'] == null ? compareUntil.subtract(_sessionHalfSlotDuration * 2) : DateTime.parse(json['olderSlotStart']! as String).toUtc(), windowDays: (json['windowDays'] as num).toInt(), canStepOlder: json['canStepOlder'] as bool? ?? false, canStepNewer: json['canStepNewer'] as bool? ?? false, stepOlderCompareUntil: _parseOptionalUtc(json['stepOlderCompareUntil']), stepNewerCompareUntil: _parseOptionalUtc(json['stepNewerCompareUntil']), assets: raw .map( (dynamic item) => QuestionAuditAsset.fromJson(item as Map), ) .toList(growable: false), ); } } DateTime? _parseOptionalUtc(dynamic raw) { if (raw == null) { return null; } return DateTime.parse(raw as String).toUtc(); }