-
Notifications
You must be signed in to change notification settings - Fork 117
Expand file tree
/
Copy pathupd_aggregate.h
More file actions
226 lines (202 loc) · 7.29 KB
/
upd_aggregate.h
File metadata and controls
226 lines (202 loc) · 7.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#pragma once
#include "oracle.h"
#include "model/price_model.h"
#include "model/price_model.c" /* FIXME: HACK TO DEAL WITH DOCKER LINKAGE ISSUES */
#include "pd.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct pc_qset
{
pd_t iprice_[PC_NUM_COMP];
pd_t uprice_[PC_NUM_COMP];
pd_t lprice_[PC_NUM_COMP];
pd_t weight_[PC_NUM_COMP];
int64_t decay_[1+PC_MAX_SEND_LATENCY];
int64_t fact_[PC_FACTOR_SIZE];
} pc_qset_t;
// initialize quote-set temporary data in heap area
static pc_qset_t *qset_new()
{
// allocate off heap
pc_qset_t *qs = (pc_qset_t*)PC_HEAP_START;
// sqrt of numbers 1 to 25 for decaying conf. interval based on slot delay
qs->decay_[0] = 1000000000L;
qs->decay_[1] = 1000000000L;
qs->decay_[2] = 1414213562L;
qs->decay_[3] = 1732050807L;
qs->decay_[4] = 2000000000L;
qs->decay_[5] = 2236067977L;
qs->decay_[6] = 2449489742L;
qs->decay_[7] = 2645751311L;
qs->decay_[8] = 2828427124L;
qs->decay_[9] = 3000000000L;
qs->decay_[10] = 3162277660L;
qs->decay_[11] = 3316624790L;
qs->decay_[12] = 3464101615L;
qs->decay_[13] = 3605551275L;
qs->decay_[14] = 3741657386L;
qs->decay_[15] = 3872983346L;
qs->decay_[16] = 4000000000L;
qs->decay_[17] = 4123105625L;
qs->decay_[18] = 4242640687L;
qs->decay_[19] = 4358898943L;
qs->decay_[20] = 4472135954L;
qs->decay_[21] = 4582575694L;
qs->decay_[22] = 4690415759L;
qs->decay_[23] = 4795831523L;
qs->decay_[24] = 4898979485L;
qs->decay_[25] = 5000000000L;
// powers of 10 for use in decimal arithmetic scaling
qs->fact_[0] = 1L;
qs->fact_[1] = 10L;
qs->fact_[2] = 100L;
qs->fact_[3] = 1000L;
qs->fact_[4] = 10000L;
qs->fact_[5] = 100000L;
qs->fact_[6] = 1000000L;
qs->fact_[7] = 10000000L;
qs->fact_[8] = 100000000L;
qs->fact_[9] = 1000000000L;
qs->fact_[10] = 10000000000L;
qs->fact_[11] = 100000000000L;
qs->fact_[12] = 1000000000000L;
qs->fact_[13] = 10000000000000L;
qs->fact_[14] = 100000000000000L;
qs->fact_[15] = 1000000000000000L;
qs->fact_[16] = 10000000000000000L;
qs->fact_[17] = 100000000000000000L;
return qs;
}
static void upd_ema(
pc_ema_t *ptr, pd_t *val, pd_t *conf, int64_t nslot, pc_qset_t *qs, int32_t expo
)
{
pd_t numer[1], denom[1], cwgt[1], wval[1], decay[1], diff[1], one[1];
pd_new( one, 100000000L, -8 );
if ( conf->v_ ) {
pd_div( cwgt, one, conf );
} else {
pd_set( cwgt, one );
}
if ( nslot > PD_EMA_MAX_DIFF ) {
// initial condition
pd_mul( numer, val, cwgt );
pd_set( denom, cwgt );
} else {
// compute decay factor
pd_new( diff, nslot, 0 );
pd_new( decay, PD_EMA_DECAY, PD_EMA_EXPO );
pd_mul( decay, decay, diff );
pd_add( decay, decay, one, qs->fact_ );
// compute numer/denom and new value from decay factor
pd_load( numer, ptr->numer_ );
pd_load( denom, ptr->denom_ );
pd_mul( numer, numer, decay );
pd_mul( wval, val, cwgt );
pd_add( numer, numer, wval, qs->fact_ );
pd_mul( denom, denom, decay );
pd_add( denom, denom, cwgt, qs->fact_ );
pd_div( val, numer, denom );
}
// adjust and store results
pd_adjust( val, expo, qs->fact_ );
ptr->val_ = val->v_;
int64_t numer1, denom1;
if ( pd_store( &numer1, numer ) && pd_store( &denom1, denom ) ) {
ptr->numer_ = numer1;
ptr->denom_ = denom1;
}
}
static inline void upd_twap(
pc_price_t *ptr, int64_t nslots )
{
pc_qset_t *qs = qset_new( );
pd_t px[1], conf[1];
pd_new_scale( px, ptr->agg_.price_, ptr->expo_ );
pd_new_scale( conf, ( int64_t )( ptr->agg_.conf_ ), ptr->expo_ );
upd_ema( &ptr->twap_, px, conf, nslots, qs, ptr->expo_ );
upd_ema( &ptr->twac_, conf, conf, nslots, qs, ptr->expo_ );
}
// update aggregate price
static inline bool upd_aggregate( pc_price_t *ptr, uint64_t slot, int64_t timestamp )
{
// Update the value of the previous price, if it had TRADING status.
if ( ptr->agg_.status_ == PC_STATUS_TRADING ) {
ptr->prev_slot_ = ptr->agg_.pub_slot_;
ptr->prev_price_ = ptr->agg_.price_;
ptr->prev_conf_ = ptr->agg_.conf_;
ptr->prev_timestamp_ = ptr->timestamp_;
}
// update aggregate details ready for next slot
ptr->valid_slot_ = ptr->agg_.pub_slot_;// valid slot-time of agg. price
ptr->agg_.pub_slot_ = slot; // publish slot-time of agg. price
ptr->timestamp_ = timestamp;
// identify valid quotes
// compute the aggregate prices and ranges
int64_t agg_price;
int64_t agg_conf;
{
uint32_t numv = 0;
uint32_t nprcs = (uint32_t)0;
int64_t prcs[ PC_NUM_COMP * 3 ]; // ~0.75KiB for current PC_NUM_COMP (FIXME: DOUBLE CHECK THIS FITS INTO STACK FRAME LIMIT)
bool allow_zero_ci = (ptr->flags & 0x4) != 0;
for ( uint32_t i = 0; i != ptr->num_; ++i ) {
pc_price_comp_t *iptr = &ptr->comp_[i];
// copy contributing price to aggregate snapshot
iptr->agg_ = iptr->latest_;
// add quote to sorted permutation array if it is valid
int64_t slot_diff = ( int64_t )slot - ( int64_t )( iptr->agg_.pub_slot_ );
int64_t price = iptr->agg_.price_;
int64_t conf = ( int64_t )( iptr->agg_.conf_ );
int64_t max_latency = ptr->max_latency_ ? ptr->max_latency_ : PC_MAX_SEND_LATENCY;
if ( iptr->agg_.status_ == PC_STATUS_TRADING &&
// Only accept confidence of zero if the flag is set
(allow_zero_ci || conf > 0) &&
// these checks ensure that price - conf and price + conf do not overflow.
(INT64_MIN + conf) <= price && price <= (INT64_MAX-conf) &&
// slot_diff is implicitly >= 0 due to the check in Rust code ensuring publishing_slot is always less than or equal to the current slot.
slot_diff <= max_latency ) {
numv += 1;
prcs[ nprcs++ ] = price - conf;
prcs[ nprcs++ ] = price;
prcs[ nprcs++ ] = price + conf;
}
}
// too few valid quotes
ptr->num_qt_ = numv;
if ( numv == 0 || numv < ptr->min_pub_ ) {
ptr->agg_.status_ = PC_STATUS_UNKNOWN;
return false;
}
// evaluate the model to get the p25/p50/p75 prices
// note: numv>0 and nprcs = 3*numv at this point
int64_t agg_p25;
int64_t agg_p75;
int64_t scratch[ PC_NUM_COMP * 3 ]; // ~0.75KiB for current PC_NUM_COMP (FIXME: DOUBLE CHECK THIS FITS INTO STACK FRAME LIMIT)
price_model_core( (uint64_t)nprcs, prcs, &agg_p25, &agg_price, &agg_p75, scratch );
// get the left and right confidences
// note that as valid quotes have positive prices currently and
// agg_p25, agg_price, agg_p75 are ordered, this calculation can't
// overflow / underflow
int64_t agg_conf_left = agg_price - agg_p25;
int64_t agg_conf_right = agg_p75 - agg_price;
// use the larger of the left and right confidences
agg_conf = agg_conf_right > agg_conf_left ? agg_conf_right : agg_conf_left;
// when zero CI is not allowed, the confidence should not be zero.
// and this check is not necessary, but we do it anyway to be safe.
if( (!allow_zero_ci) && agg_conf <= (int64_t)0 ) {
ptr->agg_.status_ = PC_STATUS_UNKNOWN;
return false;
}
}
// update status and publish slot of last trading status price
ptr->agg_.status_ = PC_STATUS_TRADING;
ptr->last_slot_ = slot;
ptr->agg_.price_ = agg_price;
ptr->agg_.conf_ = (uint64_t)agg_conf;
return true;
}
#ifdef __cplusplus
}
#endif