1- use std:: collections:: { HashMap , VecDeque } ;
1+ use std:: collections:: { BTreeMap , VecDeque } ;
22
33use chrono:: { DateTime , TimeDelta , Utc } ;
44use derive_more:: { Deref , DerefMut } ;
5- use eyre:: { Report , Result } ;
6- use v_utils:: trades:: { Asset , Kline , Pair , Timeframe } ;
5+ use eyre:: { Report , Result , bail} ;
6+ use serde_json:: json;
7+ use v_utils:: {
8+ trades:: { Asset , Kline , Pair , Timeframe } ,
9+ utils:: filter_nulls,
10+ } ;
711
812//TODO!!!!!!!!!!!!!: klines switch to defining the range via an Enum over either limit either start and end times
913
@@ -15,7 +19,7 @@ pub trait Exchange {
1519 fn exchange_info ( & self , m : Self :: M ) -> impl std:: future:: Future < Output = Result < ExchangeInfo > > + Send ;
1620
1721 //? should I have Self::Pair too? Like to catch the non-existent ones immediately? Although this would increase the error surface on new listings.
18- fn klines ( & self , pair : Pair , tf : Timeframe , range : KlinesRequestRange , m : Self :: M ) -> impl std:: future:: Future < Output = Result < Klines > > + Send ;
22+ fn klines ( & self , pair : Pair , tf : Timeframe , range : RequestRange , m : Self :: M ) -> impl std:: future:: Future < Output = Result < Klines > > + Send ;
1923
2024 /// If no pairs are specified, returns for all;
2125 fn prices ( & self , pairs : Option < Vec < Pair > > , m : Self :: M ) -> impl std:: future:: Future < Output = Result < Vec < ( Pair , f64 ) > > > + Send ;
@@ -148,68 +152,122 @@ impl TryFrom<Klines> for FullKlines {
148152 todo ! ( ) ;
149153 }
150154}
155+ //,}}}
151156
157+ // RequestRange {{{
152158#[ derive( Clone , Debug , Copy ) ]
153- pub enum KlinesRequestRange {
159+ pub enum RequestRange {
154160 /// Preferred way of defining the range
155161 StartEnd { start : DateTime < Utc > , end : Option < DateTime < Utc > > } ,
156162 /// For quick and dirty
157163 //TODO!: have it contain an enum, with either exact value, either just `Max`, then each exchange matches on it
158164 Limit ( u32 ) ,
159165}
160- impl Default for KlinesRequestRange {
166+ impl RequestRange {
167+ pub fn ensure_allowed ( & self , allowed : std:: ops:: RangeInclusive < u32 > , tf : Timeframe ) -> Result < ( ) > {
168+ match self {
169+ RequestRange :: StartEnd { start, end } =>
170+ if let Some ( end) = end {
171+ if start > end {
172+ bail ! ( "Start time is greater than end time" ) ;
173+ }
174+ let effective_limit = ( ( * end - start) . num_milliseconds ( ) / tf. duration ( ) . num_milliseconds ( ) ) as u32 ;
175+ if effective_limit > * allowed. end ( ) {
176+ return Err ( OutOfRangeError :: new ( allowed, effective_limit) . into ( ) ) ;
177+ }
178+ } ,
179+ RequestRange :: Limit ( limit) =>
180+ if !allowed. contains ( limit) {
181+ return Err ( OutOfRangeError :: new ( allowed, * limit) . into ( ) ) ;
182+ } ,
183+ }
184+ Ok ( ( ) )
185+ }
186+
187+ //XXX
188+ //TODO!!!!!!!!!: MUST be generic over Market. But with current Market representation is impossible.
189+ pub fn serialize ( & self ) -> serde_json:: Value {
190+ filter_nulls ( match self {
191+ RequestRange :: StartEnd { start, end } => json ! ( {
192+ "startTime" : start. timestamp_millis( ) ,
193+ "endTime" : end. map( |dt| dt. timestamp_millis( ) ) ,
194+ } ) ,
195+ RequestRange :: Limit ( limit) => json ! ( {
196+ "limit" : limit,
197+ } ) ,
198+ } )
199+ }
200+ }
201+ impl Default for RequestRange {
161202 fn default ( ) -> Self {
162- KlinesRequestRange :: StartEnd {
203+ RequestRange :: StartEnd {
163204 start : DateTime :: default ( ) ,
164205 end : None ,
165206 }
166207 }
167208}
168- impl From < DateTime < Utc > > for KlinesRequestRange {
209+ impl From < DateTime < Utc > > for RequestRange {
169210 fn from ( value : DateTime < Utc > ) -> Self {
170- KlinesRequestRange :: StartEnd { start : value, end : None }
211+ RequestRange :: StartEnd { start : value, end : None }
171212 }
172213}
173214/// funky
174- impl From < TimeDelta > for KlinesRequestRange {
215+ impl From < TimeDelta > for RequestRange {
175216 fn from ( value : TimeDelta ) -> Self {
176217 let now = Utc :: now ( ) ;
177- KlinesRequestRange :: StartEnd { start : now - value, end : None }
218+ RequestRange :: StartEnd { start : now - value, end : None }
178219 }
179220}
180- impl From < u32 > for KlinesRequestRange {
221+ impl From < u32 > for RequestRange {
181222 fn from ( value : u32 ) -> Self {
182- KlinesRequestRange :: Limit ( value)
223+ RequestRange :: Limit ( value)
183224 }
184225}
185- impl From < i32 > for KlinesRequestRange {
226+ impl From < i32 > for RequestRange {
186227 fn from ( value : i32 ) -> Self {
187- KlinesRequestRange :: Limit ( value as u32 )
228+ RequestRange :: Limit ( value as u32 )
188229 }
189230}
190- impl From < u16 > for KlinesRequestRange {
231+ impl From < u16 > for RequestRange {
191232 fn from ( value : u16 ) -> Self {
192- KlinesRequestRange :: Limit ( value as u32 )
233+ RequestRange :: Limit ( value as u32 )
193234 }
194235}
195- impl From < u8 > for KlinesRequestRange {
236+ impl From < u8 > for RequestRange {
196237 fn from ( value : u8 ) -> Self {
197- KlinesRequestRange :: Limit ( value as u32 )
238+ RequestRange :: Limit ( value as u32 )
198239 }
199240}
200- impl From < ( DateTime < Utc > , DateTime < Utc > ) > for KlinesRequestRange {
241+ impl From < ( DateTime < Utc > , DateTime < Utc > ) > for RequestRange {
201242 fn from ( value : ( DateTime < Utc > , DateTime < Utc > ) ) -> Self {
202- KlinesRequestRange :: StartEnd { start : value. 0 , end : Some ( value. 1 ) }
243+ RequestRange :: StartEnd { start : value. 0 , end : Some ( value. 1 ) }
203244 }
204245}
205- impl From < ( i64 , i64 ) > for KlinesRequestRange {
246+ impl From < ( i64 , i64 ) > for RequestRange {
206247 fn from ( value : ( i64 , i64 ) ) -> Self {
207- KlinesRequestRange :: StartEnd {
248+ RequestRange :: StartEnd {
208249 start : DateTime :: from_timestamp_millis ( value. 0 ) . unwrap ( ) ,
209250 end : Some ( DateTime :: from_timestamp_millis ( value. 1 ) . unwrap ( ) ) ,
210251 }
211252 }
212253}
254+
255+ #[ derive( derive_more:: Debug , derive_new:: new) ]
256+ pub struct OutOfRangeError {
257+ allowed : std:: ops:: RangeInclusive < u32 > ,
258+ provided : u32 ,
259+ }
260+ //TODO!: generalize to both create,display and check for Ranges defined by time too.
261+ impl std:: fmt:: Display for OutOfRangeError {
262+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
263+ write ! (
264+ f,
265+ "Effective provided limit is out of range (could be translated from Start:End / tf). Allowed: {:?}, provided: {}" ,
266+ self . allowed, self . provided
267+ )
268+ }
269+ }
270+ impl std:: error:: Error for OutOfRangeError { }
213271//,}}}
214272
215273#[ derive( Clone , Debug , Default , Copy ) ]
@@ -227,7 +285,7 @@ pub struct AssetBalance {
227285#[ derive( Clone , Debug , Default ) ]
228286pub struct ExchangeInfo {
229287 pub server_time : DateTime < Utc > ,
230- pub pairs : HashMap < Pair , PairInfo > ,
288+ pub pairs : BTreeMap < Pair , PairInfo > ,
231289}
232290impl ExchangeInfo {
233291 pub fn usdt_pairs ( & self ) -> impl Iterator < Item = Pair > {
0 commit comments