40template <
typename T>
class OptionSeries final {
43 std::int32_t expiration_ = 0;
44 std::int32_t lastTrade_ = 0;
45 double multiplier_ = 0.0;
47 std::string additionalUnderlyings_{};
49 std::string optionType_{};
50 std::string expirationStyle_{};
51 std::string settlementStyle_{};
54 std::map<
double, std::shared_ptr<T>> calls_{};
55 std::map<
double, std::shared_ptr<T>> puts_{};
57 mutable std::vector<
double> strikes_{};
61
62
63
64
68
69
70
71
72
78
79
80
81
82
88
89
90
91
92
98
99
100
101
102
108
109
110
111
112
113
114
115
116
117
118
119
121 return additionalUnderlyings_;
125
126
127
128
129
130
131
132
133
134
140
141
142
143
144
145
146
147
148
149
150
151
152
153
159
160
161
162
164 return expirationStyle_;
168
169
170
171
173 return settlementStyle_;
177
178
179
180
181
182
183
184
185
186
192
193
194
195
196 const std::map<
double, std::shared_ptr<T>> &getCalls()
const {
201
202
203
204
205 const std::map<
double, std::shared_ptr<T>> &getPuts()
const {
210
211
212
213
214 const std::vector<
double> &getStrikes()
const {
215 if (strikes_.empty()) {
216 std::set<
double> strikesSet{};
218 for (
const auto &call : calls_) {
219 strikesSet.insert(call.first);
222 for (
const auto &put : puts_) {
223 strikesSet.insert(put.first);
226 strikes_.assign(strikesSet.begin(), strikesSet.end());
233
234
235
236
237
238
239 std::vector<
double> getNStrikesAround(std::size_t n,
double strike)
const {
240 const auto &strikesVector = getStrikes();
241 const auto it = std::lower_bound(strikesVector.begin(), strikesVector.end(), strike);
242 const auto index =
static_cast<std::size_t>(std::distance(strikesVector.begin(), it));
243 const std::size_t fromIndex = (index < n / 2) ? 0 : (index - n / 2);
244 const std::size_t toIndex = std::min(strikesVector.size(), fromIndex + n);
246 return std::vector<
double>(strikesVector.begin() +
static_cast<std::ptrdiff_t>(fromIndex),
247 strikesVector.begin() +
static_cast<std::ptrdiff_t>(toIndex));
250 bool operator==(
const OptionSeries &other)
const {
251 return expiration_ == other.expiration_ && lastTrade_ == other.lastTrade_ &&
252 math::doubleEquals(multiplier_, other.multiplier_) && math::doubleEquals(spc_, other.spc_) &&
253 additionalUnderlyings_ == other.additionalUnderlyings_ && expirationStyle_ == other.expirationStyle_ &&
254 mmy_ == other.mmy_ && optionType_ == other.optionType_ && cfi_ == other.cfi_ &&
255 settlementStyle_ == other.settlementStyle_;
258 bool operator!=(
const OptionSeries &other)
const {
259 return !(*
this == other);
262 bool operator<(
const OptionSeries &other)
const {
263 if (expiration_ != other.expiration_) {
264 return expiration_ < other.expiration_;
267 if (lastTrade_ != other.lastTrade_) {
268 return lastTrade_ < other.lastTrade_;
271 if (
const auto result = math::doubleCompare(multiplier_, other.multiplier_); result != 0) {
275 if (
const auto result = math::doubleCompare(spc_, other.spc_); result != 0) {
279 if (additionalUnderlyings_ != other.additionalUnderlyings_) {
280 return additionalUnderlyings_ < other.additionalUnderlyings_;
283 if (mmy_ != other.mmy_) {
284 return mmy_ < other.mmy_;
287 if (optionType_ != other.optionType_) {
288 return optionType_ < other.optionType_;
291 if (expirationStyle_ != other.expirationStyle_) {
292 return expirationStyle_ < other.expirationStyle_;
295 if (settlementStyle_ != other.settlementStyle_) {
296 return settlementStyle_ < other.settlementStyle_;
299 return cfi_ < other.cfi_;
302 void addOption(
bool isCall,
double strike, std::shared_ptr<T> option) {
303 if (
auto &map = isCall ? calls_ : puts_; map.emplace(strike, option).second) {
309
310
311
312
313 std::string toString()
const {
314 std::ostringstream ss{};
316 ss <<
"expiration=" << expiration_;
318 if (lastTrade_ != 0) {
319 ss <<
", lastTrade=" << lastTrade_;
322 if (!math::exactlyEquals(multiplier_, 0.0)) {
323 ss <<
", multiplier=" << multiplier_;
326 if (!math::exactlyEquals(spc_, 0.0)) {
327 ss <<
", spc=" << spc_;
330 if (!additionalUnderlyings_.empty()) {
331 ss <<
", additionalUnderlyings=" << additionalUnderlyings_;
335 ss <<
", mmy=" << mmy_;
338 if (!optionType_.empty()) {
339 ss <<
", optionType=" << optionType_;
342 if (!expirationStyle_.empty()) {
343 ss <<
", expirationStyle=" << expirationStyle_;
346 if (!settlementStyle_.empty()) {
347 ss <<
", settlementStyle=" << settlementStyle_;
350 ss <<
", cfi=" << cfi_;
355 friend std::ostream &operator<<(std::ostream &os,
const OptionSeries &series) {
356 os << series.toString();
361 std::size_t hashCode()
const {
362 auto result =
static_cast<std::size_t>(expiration_);
364 hashCombine(result, lastTrade_);
365 hashCombine(result, std::isnan(multiplier_) ? math::NaN : multiplier_);
366 hashCombine(result, std::isnan(spc_) ? math::NaN : spc_);
367 hashCombine(result, additionalUnderlyings_);
368 hashCombine(result, mmy_);
369 hashCombine(result, optionType_);
370 hashCombine(result, expirationStyle_);
371 hashCombine(result, settlementStyle_);
372 hashCombine(result, cfi_);