• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eric15342335 / comp2113-engg1340-group-project / #45

17 Jun 2024 02:39PM UTC coverage: 73.913% (+0.4%) from 73.553%
#45

push

travis-ci

web-flow
Fix cmake exe unicode bug (#145)

Makefile and main.cpp was unrelated changes.

1462 of 1978 relevant lines covered (73.91%)

721.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.53
/src/stock.cpp
1
/// @file stock.cpp
2
/// Implementation of the Stock class.
3
/*
4
This program is free software: you can redistribute it and/or modify it under the
5
terms of the GNU Lesser General Public License as published by the Free Software
6
Foundation, either version 3 of the License, or (at your option) any later version.
7

8
This program is distributed in the hope that it will be useful, but WITHOUT ANY
9
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
10
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
11

12
You should have received a copy of the GNU Lesser General Public License along with this
13
program. If not, see <https://www.gnu.org/licenses/>.
14
*/
15
#include "stock.h"
16

17
#include "file_io.h"
18
#include "format.h"
19
#include "names.h"
20
#include "random_price.h"
21

22
#include <algorithm>
23
#include <cassert>
24
#include <fstream>
25
#include <iostream>
26

27
const int INVALID_OPERATION = -1;
28

29
Stock::Stock(void) {
140✔
30
    category = random_integer(category_list_size);
140✔
31
    name = generate_name(category, 1)[0];
140✔
32
    /** The distribution of initial stock price will be consistent across the same
33
     * categories.
34
     * Note that the value '3' is because currently init_stock_price has 3
35
     * possible input values.
36
     */
37
    price = init_stock_price(category % 3 + 1);
140✔
38
    quantity = 0;
140✔
39
    attributes[standard_deviation] = init_sd();
140✔
40
    attributes[mean] = defaultMean;
140✔
41
    attributes[lower_limit] = defaultLowerLimit;
140✔
42
    attributes[upper_limit] = defaultUpperLimit;
140✔
43
    split_count = 0;
140✔
44
    update_history();
140✔
45
}
140✔
46

47
void Stock::save(const std::string & playerName, int i) {
920✔
48
    std::string filesave;
920✔
49
    std::ofstream fout;
920✔
50
    filesave = SAVE_FOLDER_PREFIX + playerName + "/" + std::to_string(i) + "" +
1,840✔
51
               SAVE_FILE_EXTENSION_TXT; // creating the file path
920✔
52
    fout.open(filesave.c_str());
920✔
53
    fout << *this; // use operator<< to save the Stock object
920✔
54
    fout.close();
920✔
55
}
920✔
56

57
void Stock::load(const std::string & playerName, int i) {
40✔
58
    std::string fileToBeLoaded;
40✔
59
    std::ifstream fin;
40✔
60
    fileToBeLoaded = SAVE_FOLDER_PREFIX + playerName + "/" + std::to_string(i) + "" +
80✔
61
                     SAVE_FILE_EXTENSION_TXT;
40✔
62
    std::cout << "Loading " << fileToBeLoaded << " ... ";
40✔
63
    fin.open(fileToBeLoaded.c_str());
40✔
64
    // get the first line, which is category
65
    fin >> *this; // use operator>> to load the Stock object
40✔
66
    fin.close();
40✔
67
    assert(price <= STOCK_PRICE_LIMIT && "Price exceed the limit");
40✔
68
    std::cout << "done" << std::endl;
40✔
69
}
40✔
70

71
std::ostream & operator<<(std::ostream & fout, const Stock & stock) {
920✔
72
    fout << stock.category
920✔
73
         << std::endl; // literally load everything into class into file
920✔
74
    fout << stock.name << std::endl;
920✔
75
    for (unsigned int index = 0; index < stock.history.size(); index++) {
16,460✔
76
        fout << stock.history[index] << " ";
15,540✔
77
    }
78
    fout << -1 << std::endl; // -1 is the stop code for vector<float> history in
920✔
79
                             // filesave
80
    fout << stock.quantity << std::endl;
920✔
81
    fout << stock.attributes.at(standard_deviation) << " ";
920✔
82
    fout << stock.attributes.at(mean) << " ";
920✔
83
    fout << stock.attributes.at(lower_limit) << " ";
920✔
84
    fout << stock.attributes.at(upper_limit) << std::endl;
920✔
85
    fout << stock.split_count << std::endl << std::endl;
920✔
86

87
    // Save the ongoing events, separated by std::endl
88
    for (Stock_event event : stock.events) {
1,184✔
89
        fout << event << std::endl;
264✔
90
    }
264✔
91
    return fout;
920✔
92
}
93

94
std::istream & operator>>(std::istream & fin, Stock & stock) {
40✔
95
    fin >> stock.category; // line 1
40✔
96
    assert(stock.category < category_list_size && "Invalid category");
40✔
97
    //  line 2 is entirely the stock name
98
    std::getline(fin >> std::ws, stock.name);
40✔
99
    float loadedPrice;
100
    fin >> loadedPrice;
40✔
101
    // Erase the history vector and load the new history
102
    stock.history.clear();
40✔
103
    while (loadedPrice != -1) {
880✔
104
        stock.history.emplace_back(loadedPrice);
840✔
105
        fin >> loadedPrice; // line 3
840✔
106
    }
107
    // Set the price
108
    stock.price = stock.history.back();
40✔
109
    fin >> stock.quantity;                       // line 4
40✔
110
    fin >> stock.attributes[standard_deviation]; // line 5
40✔
111
    fin >> stock.attributes[mean];
40✔
112
    fin >> stock.attributes[lower_limit];
40✔
113
    fin >> stock.attributes[upper_limit];
40✔
114
    fin >> stock.split_count; // line 6
40✔
115
    // Clear the events list
116
    stock.events.clear();
40✔
117
    // Skip 2 empty lines
118
    std::string emptyLine;
40✔
119
    std::getline(fin >> std::ws, emptyLine);
40✔
120
    std::getline(fin >> std::ws, emptyLine);
40✔
121
    std::string loadedEventString;
40✔
122
    while (std::getline(fin, loadedEventString)) {
40✔
123
        Stock_event loadedEvent;
×
124
        std::istringstream(loadedEventString) >> loadedEvent;
×
125
        // Check the loaded event is valid
126
        if (loadedEvent.event_id == getStockSplitEvent().event_id) {
×
127
            stock.add_event(loadedEvent);
×
128
            continue;
×
129
        }
130
        assert(
×
131
            loadedEvent.event_id < all_stock_events.size() && "Invalid event loaded");
132
        Stock_event comparedEvent = all_stock_events[loadedEvent.event_id];
×
133
        assert(loadedEvent == comparedEvent && "Invalid event loaded");
×
134
        stock.add_event(loadedEvent);
×
135
    }
136
    return fin;
40✔
137
}
40✔
138

139
float Stock::purchase(
1✔
140
    float & balance, unsigned int amount, float trading_fees_percent) {
141
    float total_cost = price * amount * (1 + trading_fees_percent);
1✔
142
    // Check if the player has enough balance to buy the stock
143
    if (total_cost > balance && price <= 0) {
1✔
144
        return INVALID_OPERATION;
×
145
    }
146
    // Update the balance, quantity, and money_spent
147
    balance -= total_cost;
1✔
148
    quantity += amount;
1✔
149
    return total_cost;
1✔
150
}
151

152
float Stock::sell(float & balance, unsigned int amount, float trading_fees_percent) {
×
153
    // Check if the player has enough stocks to sell
154
    if (quantity < amount && price <= 0) {
×
155
        return INVALID_OPERATION;
×
156
    }
157
    // Calculate the total revenue
158
    float total_revenue = price * amount * (1 - trading_fees_percent);
×
159
    balance += total_revenue;
×
160
    quantity -= amount;
×
161
    return total_revenue;
×
162
}
163

164
std::string Stock::category_name(void) const { return category_list[category]; }
1,080✔
165

166
unsigned int Stock::num_stocks_affordable(
1,081✔
167
    float balance, float trading_fees_percent) const {
168
    float value = balance / (price * (1 + trading_fees_percent));
1,081✔
169
    return value < 0 ? 0 : static_cast<unsigned int>(value);
1,081✔
170
}
171

172
void Stock::update_history(void) {
1,020✔
173
    /** We use vector now! */
174
    history.emplace_back(price);
1,020✔
175
}
1,020✔
176

177
std::vector<float> Stock::return_most_recent_history(unsigned int rounds) {
×
178
    std::vector<float> recent_history;
×
179
    if (rounds >= history.size()) {
×
180
        return history;
×
181
    }
182
    for (unsigned int i = history.size() - rounds; i < history.size(); i++) {
×
183
        recent_history.emplace_back(history[i]);
×
184
    }
185
    return recent_history;
×
186
}
187

188
float Stock::delta_price(void) {
3,761✔
189
    // Stock prices are stored in the history array
190
    if (history.size() < 2) {
3,761✔
191
        // If there are less than two prices in the history array, return 0
192
        return 0;
240✔
193
    }
194
    // Return the change of the last two prices
195
    return history[history.size() - 1] - history[history.size() - 2];
3,521✔
196
}
197

198
float Stock::delta_price_percentage(void) {
1,080✔
199
    if (history.size() < 2 || history[history.size() - 1] < 0 ||
2,080✔
200
        history[history.size() - 2] < 0) {
1,000✔
201
        /** If there are less than two prices in the history array, return 0
202
         * If the last two prices are negative, return 0, as it is not possible to
203
         * calculate the percentage change
204
         */
205
        return 0;
80✔
206
    }
207
    return delta_price() / history[history.size() - 2];
1,000✔
208
}
209

210
void Stock::add_event(const Stock_event & event) {
135✔
211
    if (!can_add_event(event)) {
135✔
212
        // If the event is mutually exclusive with ongoing events,
213
        // ignore it and do nothing.
214
        return;
×
215
    }
216
    // If the event does not exist, add it to the std::list of events
217
    // Otherwise, update the duration of the event by deleting the old one and add the
218
    // new one.
219
    std::list<Stock_event>::iterator event_itr = events.begin();
135✔
220
    while (event_itr != events.end()) {
185✔
221
        if (*event_itr == event) {
50✔
222
            event_itr = events.erase(event_itr);
2✔
223
        }
224
        else {
225
            event_itr++;
48✔
226
        }
227
    }
228
    events.emplace_back(event);
135✔
229
}
230

231
bool Stock::can_add_event(const Stock_event & event) {
135✔
232
    std::list<Stock_event>::iterator event_itr = events.begin();
135✔
233
    while (event_itr != events.end()) {
185✔
234
        if (!event_itr->mutually_exclusive_events.empty()) {
50✔
235
            for (unsigned int i = 0; i < event_itr->mutually_exclusive_events.size();
12✔
236
                 i++) {
237
                if (event_itr->mutually_exclusive_events[i] == event.event_id) {
8✔
238
                    return false;
×
239
                }
240
            }
241
        }
242
        event_itr++;
50✔
243
    }
244
    return true;
135✔
245
}
246

247
void Stock::remove_obselete_event(void) {
880✔
248
    std::list<Stock_event>::iterator event_itr = events.begin();
880✔
249
    while (event_itr != events.end()) {
1,264✔
250
        if (event_itr->duration <= 0) {
384✔
251
            event_itr = events.erase(event_itr);
120✔
252
        }
253
        else {
254
            event_itr++;
264✔
255
        }
256
    }
257
}
880✔
258

259
float Stock::get_event_attribute(stock_modifiers attribute) {
7,840✔
260
    float sum = 0;
7,840✔
261
    std::list<Stock_event>::iterator event_itr = events.begin();
7,840✔
262
    while (event_itr != events.end()) {
10,488✔
263
        sum += event_itr->modifiers[attribute];
2,648✔
264
        event_itr++; // Bug fix: infinite loop
2,648✔
265
    }
266
    return sum;
7,840✔
267
}
268

269
Stock_event Stock::setup_STOCK_SPLIT_EVENT(void) {
×
270
    Stock_event event_copy = getStockSplitEvent();
×
271
    event_copy.text = name + event_copy.text;
×
272
    event_copy.category = category;
×
273
    return event_copy;
×
274
}
×
275

276
void Stock::next_round(void) {
880✔
277
    /** Update the price of the stock.
278
     * If the price is less than 1000, the price will increase or decrease by a random
279
     * percentage. If the price is more than 1000, the price will be halved and the
280
     * quantity will be doubled.
281
     */
282
    float price_diff = percentage_change_price(*this) / 100;
880✔
283
    // Reduce all events duration by one.
284
    std::list<Stock_event>::iterator event_itr = events.begin();
880✔
285
    while (event_itr != events.end()) {
1,264✔
286
        if (event_itr->duration > durationDecreaseMultiplier) {
384✔
287
            event_itr->duration -= durationDecreaseMultiplier;
264✔
288
        }
289
        else {
290
            event_itr->duration = 0;
120✔
291
        }
292
        event_itr++;
384✔
293
    }
294
    if (!(price * (1 + price_diff) >= STOCK_PRICE_LIMIT)) {
880✔
295
        price *= (1 + price_diff);
880✔
296
    }
297
    else {
298
        price /= 2;
×
299
        quantity *= 2;
×
300
        split_count++;
×
301
        add_event(setup_STOCK_SPLIT_EVENT());
×
302
    }
303
    remove_obselete_event();
880✔
304
    update_history();
880✔
305
}
880✔
306

307
std::vector<unsigned int> Stock::get_event_ids(void) {
1,080✔
308
    std::vector<unsigned int> event_ids;
1,080✔
309
    std::list<Stock_event>::iterator event_itr = events.begin();
1,080✔
310
    while (event_itr != events.end()) {
1,358✔
311
        event_ids.emplace_back(event_itr->event_id);
278✔
312
        event_itr++;
278✔
313
    }
314
    return event_ids;
2,160✔
315
}
×
316

317
float Stock::get_total_attribute(stock_modifiers attribute) {
3,920✔
318
    return attributes[attribute] + get_event_attribute(attribute);
3,920✔
319
}
320

321
void sortStocksList(std::vector<Stock> & stocks_list, SortingMethods sortMethod,
7✔
322
    SortingDirections sortDirection) {
323
    switch (sortMethod) {
7✔
324
        case by_name:
×
325
            std::sort(stocks_list.begin(), stocks_list.end(),
×
326
                [](Stock a, Stock b) { return a.get_name() < b.get_name(); });
×
327
            break;
×
328
        case by_category:
7✔
329
            std::sort(stocks_list.begin(), stocks_list.end(),
7✔
330
                [](Stock a, Stock b) { return a.get_category() < b.get_category(); });
689✔
331
            break;
7✔
332
        case by_price:
×
333
            std::sort(stocks_list.begin(), stocks_list.end(),
×
334
                [](Stock a, Stock b) { return a.get_price() < b.get_price(); });
×
335
            break;
×
336
        case by_quantity:
×
337
            std::sort(stocks_list.begin(), stocks_list.end(),
×
338
                [](Stock a, Stock b) { return a.get_quantity() < b.get_quantity(); });
×
339
            break;
×
340
        case by_sd:
×
341
            std::sort(stocks_list.begin(), stocks_list.end(), [](Stock a, Stock b) {
×
342
                return a.get_total_attribute(standard_deviation) <
×
343
                       b.get_total_attribute(standard_deviation);
×
344
            });
345
            break;
×
346
        case by_mean:
×
347
            std::sort(stocks_list.begin(), stocks_list.end(), [](Stock a, Stock b) {
×
348
                return a.get_total_attribute(mean) < b.get_total_attribute(mean);
×
349
            });
350
            break;
×
351
        case by_lower_limit:
×
352
            std::sort(stocks_list.begin(), stocks_list.end(), [](Stock a, Stock b) {
×
353
                return a.get_total_attribute(lower_limit) <
×
354
                       b.get_total_attribute(lower_limit);
×
355
            });
356
            break;
×
357
        case by_upper_limit:
×
358
            std::sort(stocks_list.begin(), stocks_list.end(), [](Stock a, Stock b) {
×
359
                return a.get_total_attribute(upper_limit) <
×
360
                       b.get_total_attribute(upper_limit);
×
361
            });
362
            break;
×
363
        default:
×
364
            break;
×
365
    }
366
    if (sortDirection == descending) {
7✔
367
        std::reverse(stocks_list.begin(), stocks_list.end());
×
368
    }
369
}
7✔
370

371
float Stock::calculateStockValue(const float & trading_fees_percent) const {
×
372
    return price * quantity * (1 - trading_fees_percent);
×
373
}
374

375
float Stock::calculateTradingFeesLost(const float & trading_fees_percent) const {
×
376
    return price * quantity * trading_fees_percent;
×
377
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc