-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscandi.cpp
More file actions
239 lines (203 loc) · 9.56 KB
/
scandi.cpp
File metadata and controls
239 lines (203 loc) · 9.56 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
227
228
229
230
231
232
233
234
235
236
237
238
239
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <map>
#include <vector>
#include <cmath>
#include <algorithm>
#include <tuple>
#include <iomanip>
using namespace std;
// Function to split a string by a specific delimiter
vector<string> split(const string &line, char delimiter) {
vector<string> tokens;
stringstream ss(line);
string token;
while (getline(ss, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}
// Function to compute the mean of a vector of doubles
double mean(const vector<double> &values) {
if (values.empty()) return 0.0;
double sum = 0.0;
for (double v : values) sum += v;
return sum / values.size();
}
// Function to compute the median of a vector of doubles
double median(vector<double> values) {
if (values.empty()) return 0.0;
sort(values.begin(), values.end());
if (values.size() % 2 == 0) {
return (values[values.size() / 2 - 1] + values[values.size() / 2]) / 2.0;
}
return values[values.size() / 2];
}
// Function to compute the bid-ask spread
inline double computeBidAskSpread(double askPrice, double bidPrice) {
return askPrice - bidPrice;
}
// Function to safely convert string to double with default value
double safe_stod(const string& str, double default_value = 0.0) {
if (str.empty()) return default_value;
try {
return stod(str);
} catch (const exception& e) {
return default_value;
}
}
// Function to safely convert string to int with default value
int safe_stoi(const string& str, int default_value = 0) {
if (str.empty()) return default_value;
try {
return stoi(str);
} catch (const exception& e) {
return default_value;
}
}
// Function to check if the condition indicates an auction period
bool exclusionPeriods(const double &bidPrice, const double &askPrice, const int &tradeVolume, const string &conditionCode) {
return (tradeVolume == 0 || (bidPrice > askPrice && conditionCode != "XT" && conditionCode != ""));
}
int main() {
// Open input CSV file containing tick data
ifstream infile("scandi/scandi.csv");
if (!infile.is_open()) {
cerr << "Error: Could not open input file!" << endl;
return 1;
}
cout << "Reading data file.\n";
// Map to store stock data (keyed by stock ID)
map<string, vector<tuple<double, double, double, int, int, string, int>>> stockData;
string line;
// Read and process CSV data line by line
while (getline(infile, line)) {
auto tokens = split(line, ','); // Using comma delimiter
if (tokens.size() < 16) { // Need at least 16 columns for condition codes
// Pad with empty strings if we don't have enough columns
while (tokens.size() < 16) {
tokens.push_back("");
}
}
try {
// CORRECTED INDICES based on your data description
string stockId = tokens[0]; // Column 1: Bloomberg Code/Stock identifier
double bidPrice = safe_stod(tokens[2]); // Column 3: Bid Price
double askPrice = safe_stod(tokens[3]); // Column 4: Ask Price
double tradePrice = safe_stod(tokens[4]); // Column 5: Trade Price
int tradeVolume = safe_stoi(tokens[7]); // Column 8: Trade Volume
int updateType = safe_stoi(tokens[8]); // Column 9: Update type
int time = safe_stoi(tokens[11]); // Column 12: Time in seconds past midnight
string conditionCode = tokens[14]; // Column 15: Condition codes
if (exclusionPeriods(bidPrice, askPrice, tradeVolume, conditionCode)) {
continue;
}
stockData[stockId].emplace_back(bidPrice, askPrice, tradePrice, tradeVolume, time, conditionCode, updateType);
} catch (const exception& e) {
cerr << "Error parsing line: " << line << " - " << e.what() << endl;
continue;
}
}
infile.close();
// Open output CSV file
cout << "Processing Output file.\n";
ofstream outfile("output.csv");
if (!outfile.is_open()) {
cerr << "Error: Could not create output file!" << endl;
return 1;
}
// Write header to output file
outfile << "Stock ID,Mean Time Between Trades,Median Time Between Trades,Mean Time Between Tick Changes,Median Time Between Tick Changes,"
"Longest Time Between Trades,Longest Time Between Tick Changes,Mean Bid Ask Spread,Median Bid Ask Spread,"
"Price Round Number Effect,Volume Round Number Effect,Mean Time Between Type Changes,Median between Type Changes\n";
// Process each stock's data for analysis
for (const auto &entry : stockData) {
const string &stockId = entry.first;
const vector<tuple<double, double, double, int, int, string, int>> &data = entry.second;
if (data.empty()) {
continue;
}
vector<double> tradeTimes, tickTimes, bidAskSpreads, typeTimes;
double zeroAsPriceLastDigit_Count = 0.0, nonzeroAsPriceLastDigit_Count = 0.0;
double zeroAsVolumeLastDigit_Count = 0.0, nonzeroAsVolumeLastDigit_Count = 0.0;
// For time between changes calculations
int lastTradeTime = get<4>(data[0]);
int lastTickChangeTime = get<4>(data[0]);
int lastTypeChangeTime = get<4>(data[0]);
// Process the data for the current stock
for (size_t i = 1; i < data.size(); ++i) {
const auto &prev = data[i - 1];
const auto &curr = data[i];
int currentTime = get<4>(curr);
// Calculate bid-ask spread for current quote
bidAskSpreads.push_back(computeBidAskSpread(get<1>(curr), get<0>(curr)));
// Check if this is a trade (updateType = 1)
int currTradeVolume = get<3>(curr);
int currUpdateType = get<6>(curr);
if (currUpdateType == 1) { // Only process actual trades
double tradeTimeDiff = static_cast<double>(currentTime - lastTradeTime);
// Calculate time between trades
tradeTimes.push_back(tradeTimeDiff);
lastTradeTime = currentTime;
// Volume round number effect (only for actual trades)
if (currTradeVolume % 10 == 0) {
zeroAsVolumeLastDigit_Count++;
} else {
nonzeroAsVolumeLastDigit_Count++;
}
// Price round number effect (only for actual trades)
double currTradePrice = get<2>(curr);
if (static_cast<int>(currTradePrice) % 10 == 0) {
zeroAsPriceLastDigit_Count++;
} else {
nonzeroAsPriceLastDigit_Count++;
}
}
// Time between tick changes (price changes)
if (get<2>(curr) != get<2>(prev)) {
double timeSinceLastTickChange = static_cast<double>(currentTime - lastTickChangeTime);
if (timeSinceLastTickChange > 0) { // Avoid zero intervals
tickTimes.push_back(timeSinceLastTickChange);
}
lastTickChangeTime = currentTime;
}
// Time between type changes (updateType changes)
if (get<6>(curr) != get<6>(prev)) {
double timeSinceLastTypeChange = static_cast<double>(currentTime - lastTypeChangeTime);
if (timeSinceLastTypeChange > 0) { // Avoid zero intervals
typeTimes.push_back(timeSinceLastTypeChange);
}
lastTypeChangeTime = currentTime;
}
}
// Calculate metrics
double meanTradeTime = mean(tradeTimes);
double medianTradeTime = median(tradeTimes);
double meanTickTime = mean(tickTimes);
double medianTickTime = median(tickTimes);
double longestTradeTime = tradeTimes.empty() ? 0.0 : *max_element(tradeTimes.begin(), tradeTimes.end());
double longestTickTime = tickTimes.empty() ? 0.0 : *max_element(tickTimes.begin(), tickTimes.end());
double meanBidAskSpread = mean(bidAskSpreads);
double medianBidAskSpread = median(bidAskSpreads);
double meanTypeTime = mean(typeTimes);
double medianTypeTime = median(typeTimes);
// Round number effects
double totalTradeCount = zeroAsPriceLastDigit_Count + nonzeroAsPriceLastDigit_Count;
double priceRoundNumberEffect = totalTradeCount == 0 ? 0.0 : zeroAsPriceLastDigit_Count / totalTradeCount;
double totalVolumeTradeCount = zeroAsVolumeLastDigit_Count + nonzeroAsVolumeLastDigit_Count;
double volumeRoundNumberEffect = totalVolumeTradeCount == 0 ? 0.0 : zeroAsVolumeLastDigit_Count / totalVolumeTradeCount;
// Write to output file
outfile << stockId << ","
<< meanTradeTime << "," << medianTradeTime << ","
<< meanTickTime << "," << medianTickTime << ","
<< longestTradeTime << "," << longestTickTime << ","
<< meanBidAskSpread << "," << medianBidAskSpread << ","
<< priceRoundNumberEffect << "," << volumeRoundNumberEffect << ","
<< meanTypeTime << "," << medianTypeTime << "\n";
}
outfile.close();
cout << "Processing complete. Output file 'output.csv' created.\n";
return 0;
}