Skip to content

Commit 6c23b62

Browse files
jayantkJayant Krishnamurthy
andauthored
Update definition of twap gap (#365)
Co-authored-by: Jayant Krishnamurthy <jkrishnamurthy@jumptrading.com>
1 parent dff96c1 commit 6c23b62

File tree

3 files changed

+56
-53
lines changed

3 files changed

+56
-53
lines changed

program/rust/src/accounts/price.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,19 +146,25 @@ impl PriceAccountV2 {
146146
#[repr(C)]
147147
#[derive(Copy, Clone, Pod, Zeroable)]
148148
pub struct PriceCumulative {
149-
pub price: i128, // Cumulative sum of price * slot_gap
150-
pub conf: u128, // Cumulative sum of conf * slot_gap
151-
pub num_gaps: u64, // Cumulative number of gaps in the uptime
152-
pub unused: u64, // Padding for alignment
149+
/// Cumulative sum of price * slot_gap
150+
pub price: i128,
151+
/// Cumulative sum of conf * slot_gap
152+
pub conf: u128,
153+
/// Cumulative number of slots where the price wasn't recently updated (within
154+
/// PC_MAX_SEND_LATENCY slots). This field should be used to calculate the downtime
155+
/// as a percent of slots between two times `T` and `t` as follows:
156+
/// `(T.num_down_slots - t.num_down_slots) / (T.agg_.pub_slot_ - t.agg_.pub_slot_)`
157+
pub num_down_slots: u64,
158+
/// Padding for alignment
159+
pub unused: u64,
153160
}
154161

155162
impl PriceCumulative {
156163
pub fn update(&mut self, price: i64, conf: u64, slot_gap: u64) {
157164
self.price += i128::from(price) * i128::from(slot_gap);
158165
self.conf += u128::from(conf) * u128::from(slot_gap);
159-
if slot_gap > PC_MAX_SEND_LATENCY.into() {
160-
self.num_gaps += 1;
161-
}
166+
// This is expected to saturate at 0 most of the time (while the feed is up).
167+
self.num_down_slots += slot_gap.saturating_sub(PC_MAX_SEND_LATENCY.into());
162168
}
163169
}
164170

program/rust/src/tests/test_twap.rs

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ impl Arbitrary for DataEvent {
4747
#[quickcheck]
4848
fn test_twap(input: Vec<DataEvent>) -> bool {
4949
let mut price_cumulative = PriceCumulative {
50-
price: 0,
51-
conf: 0,
52-
num_gaps: 0,
53-
unused: 0,
50+
price: 0,
51+
conf: 0,
52+
num_down_slots: 0,
53+
unused: 0,
5454
};
5555

5656
let mut data = Vec::<DataEvent>::new();
@@ -60,7 +60,7 @@ fn test_twap(input: Vec<DataEvent>) -> bool {
6060
data.push(data_event);
6161
price_cumulative.check_price(data.as_slice());
6262
price_cumulative.check_conf(data.as_slice());
63-
price_cumulative.check_num_gaps(data.as_slice());
63+
price_cumulative.check_num_down_slots(data.as_slice());
6464
price_cumulative.check_unused();
6565
}
6666

@@ -85,15 +85,15 @@ impl PriceCumulative {
8585
self.conf
8686
);
8787
}
88-
pub fn check_num_gaps(&self, data: &[DataEvent]) {
88+
pub fn check_num_down_slots(&self, data: &[DataEvent]) {
8989
assert_eq!(
9090
data.iter()
9191
.fold(0, |acc, x| if x.slot_gap > PC_MAX_SEND_LATENCY.into() {
92-
acc + 1
92+
acc + (x.slot_gap - PC_MAX_SEND_LATENCY as u64)
9393
} else {
9494
acc
9595
}),
96-
self.num_gaps
96+
self.num_down_slots
9797
);
9898
}
9999
pub fn check_unused(&self) {
@@ -104,10 +104,10 @@ impl PriceCumulative {
104104
#[test]
105105
fn test_twap_unit() {
106106
let mut price_cumulative = PriceCumulative {
107-
price: 1,
108-
conf: 2,
109-
num_gaps: 3,
110-
unused: 0,
107+
price: 1,
108+
conf: 2,
109+
num_down_slots: 3,
110+
unused: 0,
111111
};
112112

113113
let data = vec![
@@ -131,26 +131,26 @@ fn test_twap_unit() {
131131
price_cumulative.update(data[0].price, data[0].conf, data[0].slot_gap);
132132
assert_eq!(price_cumulative.price, 5);
133133
assert_eq!(price_cumulative.conf, 10);
134-
assert_eq!(price_cumulative.num_gaps, 3);
134+
assert_eq!(price_cumulative.num_down_slots, 3);
135135
assert_eq!(price_cumulative.unused, 0);
136136

137137
price_cumulative.update(data[1].price, data[1].conf, data[1].slot_gap);
138138
assert_eq!(price_cumulative.price, 9_223_372_036_854_775_812i128);
139139
assert_eq!(price_cumulative.conf, 18_446_744_073_709_551_625u128);
140-
assert_eq!(price_cumulative.num_gaps, 3);
140+
assert_eq!(price_cumulative.num_down_slots, 3);
141141
assert_eq!(price_cumulative.unused, 0);
142142

143143
price_cumulative.update(data[2].price, data[2].conf, data[2].slot_gap);
144144
assert_eq!(price_cumulative.price, 9_223_372_036_854_775_512i128);
145145
assert_eq!(price_cumulative.conf, 18_446_744_073_709_551_745u128);
146-
assert_eq!(price_cumulative.num_gaps, 4);
146+
assert_eq!(price_cumulative.num_down_slots, 8);
147147
assert_eq!(price_cumulative.unused, 0);
148148

149149
let mut price_cumulative_overflow = PriceCumulative {
150-
price: 0,
151-
conf: 0,
152-
num_gaps: 0,
153-
unused: 0,
150+
price: 0,
151+
conf: 0,
152+
num_down_slots: 0,
153+
unused: 0,
154154
};
155155
price_cumulative_overflow.update(i64::MIN, u64::MAX, u64::MAX);
156156
assert_eq!(
@@ -161,7 +161,10 @@ fn test_twap_unit() {
161161
price_cumulative_overflow.conf,
162162
u128::MAX - 2 * u128::from(u64::MAX)
163163
);
164-
assert_eq!(price_cumulative_overflow.num_gaps, 1);
164+
assert_eq!(
165+
price_cumulative_overflow.num_down_slots,
166+
u64::MAX - PC_MAX_SEND_LATENCY as u64
167+
);
165168
assert_eq!(price_cumulative_overflow.unused, 0);
166169
}
167170

@@ -181,17 +184,17 @@ fn test_twap_with_price_account() {
181184
pub_slot_: 5,
182185
};
183186
price_data.price_cumulative = PriceCumulative {
184-
price: 1,
185-
conf: 2,
186-
num_gaps: 3,
187-
unused: 0,
187+
price: 1,
188+
conf: 2,
189+
num_down_slots: 3,
190+
unused: 0,
188191
};
189192
price_data.prev_slot_ = 3;
190193
price_data.update_price_cumulative().unwrap();
191194

192195
assert_eq!(price_data.price_cumulative.price, 1 - 2 * 10);
193196
assert_eq!(price_data.price_cumulative.conf, 2 + 2 * 5);
194-
assert_eq!(price_data.price_cumulative.num_gaps, 3);
197+
assert_eq!(price_data.price_cumulative.num_down_slots, 3);
195198

196199
// Slot decreases
197200
price_data.agg_ = PriceInfo {
@@ -205,7 +208,7 @@ fn test_twap_with_price_account() {
205208

206209
assert_eq!(price_data.price_cumulative.price, 1 - 2 * 10);
207210
assert_eq!(price_data.price_cumulative.conf, 2 + 2 * 5);
208-
assert_eq!(price_data.price_cumulative.num_gaps, 3);
211+
assert_eq!(price_data.price_cumulative.num_down_slots, 3);
209212

210213
// Status is not trading
211214
price_data.agg_ = PriceInfo {
@@ -224,13 +227,13 @@ fn test_twap_with_price_account() {
224227

225228
assert_eq!(price_data.price_cumulative.price, 1 - 2 * 10);
226229
assert_eq!(price_data.price_cumulative.conf, 2 + 2 * 5);
227-
assert_eq!(price_data.price_cumulative.num_gaps, 3);
230+
assert_eq!(price_data.price_cumulative.num_down_slots, 3);
228231

229232
// Back to normal behavior
230233
price_data.agg_.status_ = PC_STATUS_TRADING;
231234
price_data.update_price_cumulative().unwrap();
232235

233236
assert_eq!(price_data.price_cumulative.price, 1 - 2 * 10 + 1);
234237
assert_eq!(price_data.price_cumulative.conf, 2 + 2 * 5 + 2);
235-
assert_eq!(price_data.price_cumulative.num_gaps, 3);
238+
assert_eq!(price_data.price_cumulative.num_down_slots, 3);
236239
}

program/rust/src/tests/test_upd_price_v2.rs

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ fn test_upd_price_v2() {
8484

8585
assert_eq!(price_data.price_cumulative.price, 0);
8686
assert_eq!(price_data.price_cumulative.conf, 0);
87-
assert_eq!(price_data.price_cumulative.num_gaps, 0);
87+
assert_eq!(price_data.price_cumulative.num_down_slots, 0);
8888
}
8989

9090
// add some prices for current slot - get rejected
@@ -117,7 +117,7 @@ fn test_upd_price_v2() {
117117

118118
assert_eq!(price_data.price_cumulative.price, 0);
119119
assert_eq!(price_data.price_cumulative.conf, 0);
120-
assert_eq!(price_data.price_cumulative.num_gaps, 0);
120+
assert_eq!(price_data.price_cumulative.num_down_slots, 0);
121121
}
122122

123123
// add next price in new slot triggering snapshot and aggregate calc
@@ -149,7 +149,7 @@ fn test_upd_price_v2() {
149149

150150
assert_eq!(price_data.price_cumulative.price, 3 * 42);
151151
assert_eq!(price_data.price_cumulative.conf, 3 * 2);
152-
assert_eq!(price_data.price_cumulative.num_gaps, 0);
152+
assert_eq!(price_data.price_cumulative.num_down_slots, 0);
153153
}
154154

155155
// next price doesnt change but slot does
@@ -180,7 +180,7 @@ fn test_upd_price_v2() {
180180

181181
assert_eq!(price_data.price_cumulative.price, 3 * 42 + 81);
182182
assert_eq!(price_data.price_cumulative.conf, 3 * 2 + 2);
183-
assert_eq!(price_data.price_cumulative.num_gaps, 0);
183+
assert_eq!(price_data.price_cumulative.num_down_slots, 0);
184184
}
185185

186186
// next price doesnt change and neither does aggregate but slot does
@@ -211,7 +211,7 @@ fn test_upd_price_v2() {
211211

212212
assert_eq!(price_data.price_cumulative.price, 3 * 42 + 81 * 2);
213213
assert_eq!(price_data.price_cumulative.conf, 3 * 2 + 2 * 2);
214-
assert_eq!(price_data.price_cumulative.num_gaps, 0);
214+
assert_eq!(price_data.price_cumulative.num_down_slots, 0);
215215
}
216216

217217
// try to publish back-in-time
@@ -244,7 +244,7 @@ fn test_upd_price_v2() {
244244

245245
assert_eq!(price_data.price_cumulative.price, 3 * 42 + 81 * 2);
246246
assert_eq!(price_data.price_cumulative.conf, 3 * 2 + 2 * 2);
247-
assert_eq!(price_data.price_cumulative.num_gaps, 0);
247+
assert_eq!(price_data.price_cumulative.num_down_slots, 0);
248248
}
249249

250250
populate_instruction(&mut instruction_data, 50, 20, 5);
@@ -283,7 +283,7 @@ fn test_upd_price_v2() {
283283

284284
assert_eq!(price_data.price_cumulative.price, 3 * 42 + 81 * 3);
285285
assert_eq!(price_data.price_cumulative.conf, 3 * 2 + 2 * 3);
286-
assert_eq!(price_data.price_cumulative.num_gaps, 0);
286+
assert_eq!(price_data.price_cumulative.num_down_slots, 0);
287287
}
288288

289289
// Crank one more time and aggregate should be unknown
@@ -315,7 +315,7 @@ fn test_upd_price_v2() {
315315

316316
assert_eq!(price_data.price_cumulative.price, 3 * 42 + 81 * 3);
317317
assert_eq!(price_data.price_cumulative.conf, 3 * 2 + 2 * 3);
318-
assert_eq!(price_data.price_cumulative.num_gaps, 0);
318+
assert_eq!(price_data.price_cumulative.num_down_slots, 0);
319319
}
320320

321321
// Negative prices are accepted
@@ -347,7 +347,7 @@ fn test_upd_price_v2() {
347347

348348
assert_eq!(price_data.price_cumulative.price, 3 * 42 + 81 * 3);
349349
assert_eq!(price_data.price_cumulative.conf, 3 * 2 + 2 * 3);
350-
assert_eq!(price_data.price_cumulative.num_gaps, 0);
350+
assert_eq!(price_data.price_cumulative.num_down_slots, 0);
351351
}
352352

353353
// Crank again for aggregate
@@ -379,7 +379,7 @@ fn test_upd_price_v2() {
379379

380380
assert_eq!(price_data.price_cumulative.price, 3 * 42 + 81 * 3 - 100 * 3);
381381
assert_eq!(price_data.price_cumulative.conf, 3 * 2 + 2 * 3 + 3);
382-
assert_eq!(price_data.price_cumulative.num_gaps, 0);
382+
assert_eq!(price_data.price_cumulative.num_down_slots, 0);
383383
}
384384

385385
// Big gap
@@ -412,15 +412,9 @@ fn test_upd_price_v2() {
412412

413413
assert_eq!(price_data.price_cumulative.price, 3 * 42 + 81 * 3 - 100 * 3);
414414
assert_eq!(price_data.price_cumulative.conf, 3 * 2 + 2 * 3 + 3);
415-
assert_eq!(price_data.price_cumulative.num_gaps, 0);
415+
assert_eq!(price_data.price_cumulative.num_down_slots, 0);
416416
}
417417

418-
// Big gap
419-
420-
populate_instruction(&mut instruction_data, 60, 4, 50);
421-
update_clock_slot(&mut clock_account, 50);
422-
423-
424418
// Crank again for aggregate
425419

426420
populate_instruction(&mut instruction_data, 55, 5, 51);
@@ -454,7 +448,7 @@ fn test_upd_price_v2() {
454448
3 * 42 + 81 * 3 - 100 * 3 + 42 * 60
455449
);
456450
assert_eq!(price_data.price_cumulative.conf, 3 * 2 + 2 * 3 + 3 + 42 * 4);
457-
assert_eq!(price_data.price_cumulative.num_gaps, 1);
451+
assert_eq!(price_data.price_cumulative.num_down_slots, 17);
458452
}
459453
}
460454

0 commit comments

Comments
 (0)