pub struct IndexAnalytics { /* private fields */ }Expand description
Компонент индекса из таблицы analytics.
Implementations§
Source§impl IndexAnalytics
impl IndexAnalytics
Sourcepub fn ticker(&self) -> &SecId
pub fn ticker(&self) -> &SecId
Тикер компонента.
Examples found in repository?
examples/actual_indexes_dump.rs (line 342)
289fn print_dump(
290 index_dumps: &[IndexDump],
291 snapshots: &HashMap<SecId, ResolvedSnapshot>,
292 missing_mapping: &[SecId],
293) {
294 println!("active indexes: {}", index_dumps.len());
295
296 let total_components = index_dumps
297 .iter()
298 .map(|index| index.components.len())
299 .sum::<usize>();
300 println!("total components across active indexes: {total_components}");
301 println!("resolved security snapshots: {}", snapshots.len());
302
303 if !missing_mapping.is_empty() {
304 let preview = missing_mapping
305 .iter()
306 .take(10)
307 .map(SecId::as_str)
308 .collect::<Vec<_>>()
309 .join(", ");
310 println!(
311 "warning: {} securities have no stock snapshot data (first 10): {preview}",
312 missing_mapping.len()
313 );
314 }
315
316 for index in index_dumps {
317 println!();
318 println!("=== {} | {} ===", index.index_id, index.short_name);
319 if let Some(first_component) = index.components.first() {
320 println!(
321 "trade_session_date={} tradingsession={} components={}",
322 first_component.trade_session_date(),
323 first_component.tradingsession(),
324 index.components.len()
325 );
326 } else {
327 println!("components=0");
328 }
329
330 println!("SECID\tTICKER\tSHORTNAME\tWEIGHT\tLAST\tLOTSIZE\tMARKET\tBOARD");
331 for component in &index.components {
332 let snapshot = snapshots.get(component.secid());
333
334 let last = format_optional_f64(snapshot.and_then(|item| item.last));
335 let lot_size = format_optional_u32(snapshot.and_then(|item| item.lot_size));
336 let market = snapshot.map_or("N/A", |item| item.market.as_ref());
337 let board = snapshot.map_or("N/A", |item| item.board.as_ref());
338
339 println!(
340 "{}\t{}\t{}\t{:.6}\t{}\t{}\t{}\t{}",
341 component.secid(),
342 component.ticker(),
343 component.shortnames(),
344 component.weight(),
345 last,
346 lot_size,
347 market,
348 board,
349 );
350 }
351 }
352}Sourcepub fn shortnames(&self) -> &str
pub fn shortnames(&self) -> &str
Краткие имена бумаг.
Examples found in repository?
examples/actual_indexes_dump.rs (line 343)
289fn print_dump(
290 index_dumps: &[IndexDump],
291 snapshots: &HashMap<SecId, ResolvedSnapshot>,
292 missing_mapping: &[SecId],
293) {
294 println!("active indexes: {}", index_dumps.len());
295
296 let total_components = index_dumps
297 .iter()
298 .map(|index| index.components.len())
299 .sum::<usize>();
300 println!("total components across active indexes: {total_components}");
301 println!("resolved security snapshots: {}", snapshots.len());
302
303 if !missing_mapping.is_empty() {
304 let preview = missing_mapping
305 .iter()
306 .take(10)
307 .map(SecId::as_str)
308 .collect::<Vec<_>>()
309 .join(", ");
310 println!(
311 "warning: {} securities have no stock snapshot data (first 10): {preview}",
312 missing_mapping.len()
313 );
314 }
315
316 for index in index_dumps {
317 println!();
318 println!("=== {} | {} ===", index.index_id, index.short_name);
319 if let Some(first_component) = index.components.first() {
320 println!(
321 "trade_session_date={} tradingsession={} components={}",
322 first_component.trade_session_date(),
323 first_component.tradingsession(),
324 index.components.len()
325 );
326 } else {
327 println!("components=0");
328 }
329
330 println!("SECID\tTICKER\tSHORTNAME\tWEIGHT\tLAST\tLOTSIZE\tMARKET\tBOARD");
331 for component in &index.components {
332 let snapshot = snapshots.get(component.secid());
333
334 let last = format_optional_f64(snapshot.and_then(|item| item.last));
335 let lot_size = format_optional_u32(snapshot.and_then(|item| item.lot_size));
336 let market = snapshot.map_or("N/A", |item| item.market.as_ref());
337 let board = snapshot.map_or("N/A", |item| item.board.as_ref());
338
339 println!(
340 "{}\t{}\t{}\t{:.6}\t{}\t{}\t{}\t{}",
341 component.secid(),
342 component.ticker(),
343 component.shortnames(),
344 component.weight(),
345 last,
346 lot_size,
347 market,
348 board,
349 );
350 }
351 }
352}Sourcepub fn secid(&self) -> &SecId
pub fn secid(&self) -> &SecId
Идентификатор инструмента компонента.
Examples found in repository?
examples/actual_indexes_dump.rs (line 96)
93fn collect_unique_secids(index_dumps: &[IndexDump]) -> HashSet<SecId> {
94 index_dumps
95 .iter()
96 .flat_map(|index| index.components.iter().map(|row| row.secid().clone()))
97 .collect()
98}
99
100fn load_security_snapshots(
101 moex_client: &Client,
102 wanted_secids: &HashSet<SecId>,
103) -> Result<(HashMap<SecId, ResolvedSnapshot>, Vec<SecId>), ExampleError> {
104 let stock_scope = moex_client
105 .stock()
106 .expect("stock engine literal must be valid");
107 let markets = with_retry(retry_policy(), || stock_scope.markets())?;
108 let mut snapshots = HashMap::with_capacity(wanted_secids.len());
109
110 'markets: for market in markets {
111 let boards = with_retry(retry_policy(), || {
112 moex_client.boards(stock_scope.engine(), market.name())
113 })?;
114
115 for board in boards.into_iter().filter(|board| board.is_traded()) {
116 let board_snapshots = load_board_snapshots_all_pages(
117 moex_client,
118 stock_scope.engine().as_str(),
119 market.name().as_str(),
120 board.boardid().as_str(),
121 )?;
122
123 for snapshot in board_snapshots {
124 if !wanted_secids.contains(snapshot.secid()) {
125 continue;
126 }
127
128 snapshots
129 .entry(snapshot.secid().clone())
130 .or_insert_with(|| ResolvedSnapshot {
131 market: market.name().as_str().to_owned().into_boxed_str(),
132 board: board.boardid().as_str().to_owned().into_boxed_str(),
133 lot_size: snapshot.lot_size(),
134 last: snapshot.last(),
135 });
136 }
137
138 if snapshots.len() == wanted_secids.len() {
139 break 'markets;
140 }
141 }
142 }
143
144 let missing_mapping = wanted_secids
145 .iter()
146 .filter(|secid| !snapshots.contains_key(*secid))
147 .cloned()
148 .collect();
149
150 Ok((snapshots, missing_mapping))
151}
152
153fn load_board_snapshots_all_pages(
154 moex_client: &Client,
155 engine: &str,
156 market: &str,
157 board: &str,
158) -> Result<Vec<SecuritySnapshot>, MoexError> {
159 let endpoint = format!("engines/{engine}/markets/{market}/boards/{board}/securities.json");
160 let mut start = 0_u32;
161 let mut snapshots = Vec::new();
162 let mut first_secid_on_previous_page = None;
163 let limit = BOARD_SNAPSHOTS_PAGE_LIMIT.to_string();
164
165 loop {
166 let payload = with_retry(retry_policy(), || {
167 moex_client
168 .raw()
169 .path(endpoint.as_str())
170 .only("securities,marketdata")
171 .columns("securities", "SECID,LOTSIZE")
172 .columns("marketdata", "SECID,LAST")
173 .param("start", start.to_string())
174 .param("limit", limit.as_str())
175 .send_payload()
176 })?;
177
178 let (page, first_secid_on_page) = parse_board_snapshots_page(&payload, endpoint.as_str())?;
179 if page.is_empty() {
180 break;
181 }
182
183 if let (Some(previous), Some(current)) =
184 (&first_secid_on_previous_page, &first_secid_on_page)
185 && previous == current
186 {
187 break;
188 }
189 first_secid_on_previous_page = first_secid_on_page;
190
191 let page_len = u32::try_from(page.len()).map_err(|_| MoexError::PaginationOverflow {
192 endpoint: endpoint.clone().into_boxed_str(),
193 start,
194 limit: BOARD_SNAPSHOTS_PAGE_LIMIT,
195 })?;
196
197 snapshots.extend(page);
198 if page_len < BOARD_SNAPSHOTS_PAGE_LIMIT {
199 break;
200 }
201 start = start
202 .checked_add(page_len)
203 .ok_or_else(|| MoexError::PaginationOverflow {
204 endpoint: endpoint.clone().into_boxed_str(),
205 start,
206 limit: BOARD_SNAPSHOTS_PAGE_LIMIT,
207 })?;
208 }
209
210 Ok(snapshots)
211}
212
213fn parse_board_snapshots_page(
214 payload: &str,
215 endpoint: &str,
216) -> Result<(Vec<SecuritySnapshot>, Option<SecId>), MoexError> {
217 let mut tables = decode::raw_tables_json(payload, endpoint)?;
218 let security_rows: Vec<RawSecuritySnapshotRow> = tables.take_rows("securities")?;
219 let marketdata_rows: Vec<RawMarketdataSnapshotRow> = tables.take_rows("marketdata")?;
220
221 let mut marketdata_by_secid = HashMap::with_capacity(marketdata_rows.len());
222 for (row, marketdata) in marketdata_rows.into_iter().enumerate() {
223 marketdata_by_secid.insert(marketdata.secid, (row, marketdata.last));
224 }
225
226 let mut first_secid_on_page = None;
227 let mut snapshots = Vec::with_capacity(security_rows.len().max(marketdata_by_secid.len()));
228
229 for (row, security) in security_rows.into_iter().enumerate() {
230 let last = marketdata_by_secid
231 .remove(security.secid.as_str())
232 .and_then(|(_, last)| last);
233 let snapshot = SecuritySnapshot::try_new(security.secid, security.lot_size, last).map_err(
234 |source| MoexError::InvalidSecuritySnapshot {
235 endpoint: endpoint.to_owned().into_boxed_str(),
236 table: "securities",
237 row,
238 source,
239 },
240 )?;
241 if first_secid_on_page.is_none() {
242 first_secid_on_page = Some(snapshot.secid().clone());
243 }
244 snapshots.push(snapshot);
245 }
246
247 for (secid, (row, last)) in marketdata_by_secid {
248 let snapshot = SecuritySnapshot::try_new(secid, None, last).map_err(|source| {
249 MoexError::InvalidSecuritySnapshot {
250 endpoint: endpoint.to_owned().into_boxed_str(),
251 table: "marketdata",
252 row,
253 source,
254 }
255 })?;
256 if first_secid_on_page.is_none() {
257 first_secid_on_page = Some(snapshot.secid().clone());
258 }
259 snapshots.push(snapshot);
260 }
261
262 Ok((snapshots, first_secid_on_page))
263}
264
265#[derive(Debug, serde::Deserialize)]
266struct RawSecuritySnapshotRow {
267 #[serde(rename = "SECID")]
268 secid: String,
269 #[serde(rename = "LOTSIZE", default)]
270 lot_size: Option<i64>,
271}
272
273#[derive(Debug, serde::Deserialize)]
274struct RawMarketdataSnapshotRow {
275 #[serde(rename = "SECID")]
276 secid: String,
277 #[serde(rename = "LAST", default)]
278 last: Option<f64>,
279}
280
281fn retry_policy() -> RetryPolicy {
282 RetryPolicy::new(
283 NonZeroU32::new(REQUEST_RETRIES)
284 .expect("REQUEST_RETRIES constant must be greater than zero"),
285 )
286 .with_delay(Duration::from_millis(RETRY_DELAY_MILLIS))
287}
288
289fn print_dump(
290 index_dumps: &[IndexDump],
291 snapshots: &HashMap<SecId, ResolvedSnapshot>,
292 missing_mapping: &[SecId],
293) {
294 println!("active indexes: {}", index_dumps.len());
295
296 let total_components = index_dumps
297 .iter()
298 .map(|index| index.components.len())
299 .sum::<usize>();
300 println!("total components across active indexes: {total_components}");
301 println!("resolved security snapshots: {}", snapshots.len());
302
303 if !missing_mapping.is_empty() {
304 let preview = missing_mapping
305 .iter()
306 .take(10)
307 .map(SecId::as_str)
308 .collect::<Vec<_>>()
309 .join(", ");
310 println!(
311 "warning: {} securities have no stock snapshot data (first 10): {preview}",
312 missing_mapping.len()
313 );
314 }
315
316 for index in index_dumps {
317 println!();
318 println!("=== {} | {} ===", index.index_id, index.short_name);
319 if let Some(first_component) = index.components.first() {
320 println!(
321 "trade_session_date={} tradingsession={} components={}",
322 first_component.trade_session_date(),
323 first_component.tradingsession(),
324 index.components.len()
325 );
326 } else {
327 println!("components=0");
328 }
329
330 println!("SECID\tTICKER\tSHORTNAME\tWEIGHT\tLAST\tLOTSIZE\tMARKET\tBOARD");
331 for component in &index.components {
332 let snapshot = snapshots.get(component.secid());
333
334 let last = format_optional_f64(snapshot.and_then(|item| item.last));
335 let lot_size = format_optional_u32(snapshot.and_then(|item| item.lot_size));
336 let market = snapshot.map_or("N/A", |item| item.market.as_ref());
337 let board = snapshot.map_or("N/A", |item| item.board.as_ref());
338
339 println!(
340 "{}\t{}\t{}\t{:.6}\t{}\t{}\t{}\t{}",
341 component.secid(),
342 component.ticker(),
343 component.shortnames(),
344 component.weight(),
345 last,
346 lot_size,
347 market,
348 board,
349 );
350 }
351 }
352}Sourcepub fn weight(&self) -> f64
pub fn weight(&self) -> f64
Вес компонента в индексе.
Examples found in repository?
examples/actual_indexes_dump.rs (line 344)
289fn print_dump(
290 index_dumps: &[IndexDump],
291 snapshots: &HashMap<SecId, ResolvedSnapshot>,
292 missing_mapping: &[SecId],
293) {
294 println!("active indexes: {}", index_dumps.len());
295
296 let total_components = index_dumps
297 .iter()
298 .map(|index| index.components.len())
299 .sum::<usize>();
300 println!("total components across active indexes: {total_components}");
301 println!("resolved security snapshots: {}", snapshots.len());
302
303 if !missing_mapping.is_empty() {
304 let preview = missing_mapping
305 .iter()
306 .take(10)
307 .map(SecId::as_str)
308 .collect::<Vec<_>>()
309 .join(", ");
310 println!(
311 "warning: {} securities have no stock snapshot data (first 10): {preview}",
312 missing_mapping.len()
313 );
314 }
315
316 for index in index_dumps {
317 println!();
318 println!("=== {} | {} ===", index.index_id, index.short_name);
319 if let Some(first_component) = index.components.first() {
320 println!(
321 "trade_session_date={} tradingsession={} components={}",
322 first_component.trade_session_date(),
323 first_component.tradingsession(),
324 index.components.len()
325 );
326 } else {
327 println!("components=0");
328 }
329
330 println!("SECID\tTICKER\tSHORTNAME\tWEIGHT\tLAST\tLOTSIZE\tMARKET\tBOARD");
331 for component in &index.components {
332 let snapshot = snapshots.get(component.secid());
333
334 let last = format_optional_f64(snapshot.and_then(|item| item.last));
335 let lot_size = format_optional_u32(snapshot.and_then(|item| item.lot_size));
336 let market = snapshot.map_or("N/A", |item| item.market.as_ref());
337 let board = snapshot.map_or("N/A", |item| item.board.as_ref());
338
339 println!(
340 "{}\t{}\t{}\t{:.6}\t{}\t{}\t{}\t{}",
341 component.secid(),
342 component.ticker(),
343 component.shortnames(),
344 component.weight(),
345 last,
346 lot_size,
347 market,
348 board,
349 );
350 }
351 }
352}Sourcepub fn tradingsession(&self) -> u8
pub fn tradingsession(&self) -> u8
Торговая сессия (1..=3).
Examples found in repository?
examples/actual_indexes_dump.rs (line 323)
289fn print_dump(
290 index_dumps: &[IndexDump],
291 snapshots: &HashMap<SecId, ResolvedSnapshot>,
292 missing_mapping: &[SecId],
293) {
294 println!("active indexes: {}", index_dumps.len());
295
296 let total_components = index_dumps
297 .iter()
298 .map(|index| index.components.len())
299 .sum::<usize>();
300 println!("total components across active indexes: {total_components}");
301 println!("resolved security snapshots: {}", snapshots.len());
302
303 if !missing_mapping.is_empty() {
304 let preview = missing_mapping
305 .iter()
306 .take(10)
307 .map(SecId::as_str)
308 .collect::<Vec<_>>()
309 .join(", ");
310 println!(
311 "warning: {} securities have no stock snapshot data (first 10): {preview}",
312 missing_mapping.len()
313 );
314 }
315
316 for index in index_dumps {
317 println!();
318 println!("=== {} | {} ===", index.index_id, index.short_name);
319 if let Some(first_component) = index.components.first() {
320 println!(
321 "trade_session_date={} tradingsession={} components={}",
322 first_component.trade_session_date(),
323 first_component.tradingsession(),
324 index.components.len()
325 );
326 } else {
327 println!("components=0");
328 }
329
330 println!("SECID\tTICKER\tSHORTNAME\tWEIGHT\tLAST\tLOTSIZE\tMARKET\tBOARD");
331 for component in &index.components {
332 let snapshot = snapshots.get(component.secid());
333
334 let last = format_optional_f64(snapshot.and_then(|item| item.last));
335 let lot_size = format_optional_u32(snapshot.and_then(|item| item.lot_size));
336 let market = snapshot.map_or("N/A", |item| item.market.as_ref());
337 let board = snapshot.map_or("N/A", |item| item.board.as_ref());
338
339 println!(
340 "{}\t{}\t{}\t{:.6}\t{}\t{}\t{}\t{}",
341 component.secid(),
342 component.ticker(),
343 component.shortnames(),
344 component.weight(),
345 last,
346 lot_size,
347 market,
348 board,
349 );
350 }
351 }
352}Sourcepub fn trade_session_date(&self) -> NaiveDate
pub fn trade_session_date(&self) -> NaiveDate
Дата торговой сессии (tradedate в источнике analytics).
Examples found in repository?
examples/actual_indexes_dump.rs (line 322)
289fn print_dump(
290 index_dumps: &[IndexDump],
291 snapshots: &HashMap<SecId, ResolvedSnapshot>,
292 missing_mapping: &[SecId],
293) {
294 println!("active indexes: {}", index_dumps.len());
295
296 let total_components = index_dumps
297 .iter()
298 .map(|index| index.components.len())
299 .sum::<usize>();
300 println!("total components across active indexes: {total_components}");
301 println!("resolved security snapshots: {}", snapshots.len());
302
303 if !missing_mapping.is_empty() {
304 let preview = missing_mapping
305 .iter()
306 .take(10)
307 .map(SecId::as_str)
308 .collect::<Vec<_>>()
309 .join(", ");
310 println!(
311 "warning: {} securities have no stock snapshot data (first 10): {preview}",
312 missing_mapping.len()
313 );
314 }
315
316 for index in index_dumps {
317 println!();
318 println!("=== {} | {} ===", index.index_id, index.short_name);
319 if let Some(first_component) = index.components.first() {
320 println!(
321 "trade_session_date={} tradingsession={} components={}",
322 first_component.trade_session_date(),
323 first_component.tradingsession(),
324 index.components.len()
325 );
326 } else {
327 println!("components=0");
328 }
329
330 println!("SECID\tTICKER\tSHORTNAME\tWEIGHT\tLAST\tLOTSIZE\tMARKET\tBOARD");
331 for component in &index.components {
332 let snapshot = snapshots.get(component.secid());
333
334 let last = format_optional_f64(snapshot.and_then(|item| item.last));
335 let lot_size = format_optional_u32(snapshot.and_then(|item| item.lot_size));
336 let market = snapshot.map_or("N/A", |item| item.market.as_ref());
337 let board = snapshot.map_or("N/A", |item| item.board.as_ref());
338
339 println!(
340 "{}\t{}\t{}\t{:.6}\t{}\t{}\t{}\t{}",
341 component.secid(),
342 component.ticker(),
343 component.shortnames(),
344 component.weight(),
345 last,
346 lot_size,
347 market,
348 board,
349 );
350 }
351 }
352}Trait Implementations§
Source§impl Clone for IndexAnalytics
impl Clone for IndexAnalytics
Source§fn clone(&self) -> IndexAnalytics
fn clone(&self) -> IndexAnalytics
Returns a duplicate of the value. Read more
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Debug for IndexAnalytics
impl Debug for IndexAnalytics
Source§impl PartialEq for IndexAnalytics
impl PartialEq for IndexAnalytics
impl StructuralPartialEq for IndexAnalytics
Auto Trait Implementations§
impl Freeze for IndexAnalytics
impl RefUnwindSafe for IndexAnalytics
impl Send for IndexAnalytics
impl Sync for IndexAnalytics
impl Unpin for IndexAnalytics
impl UnsafeUnpin for IndexAnalytics
impl UnwindSafe for IndexAnalytics
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more