/*
* Stock Market Trader Simulator — чистый C (C99), без C++.
* Сборка: gcc -std=c99 -O2 -o stock_trader_sim stock_trader_sim.c
* Windows (MinGW): то же самое.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define MAX_SYMBOL_LEN 8
#define MAX_NAME_LEN 32
#define NUM_STOCKS 5
#define STARTING_CASH 10000.0
typedef struct {
char symbol[MAX_SYMBOL_LEN];
char name[MAX_NAME_LEN];
double price;
double prev_price;
double volatility; /* доля (например 0.02 = ±2% в среднем за день) */
} Stock;
typedef struct {
int shares[NUM_STOCKS];
} Holdings;
static Stock market[NUM_STOCKS];
static Holdings holdings;
static double cash;
static int day;
static void init_market(void) {
const char *names[NUM_STOCKS] = {
"Northern Tech Inc",
"Global Ore Co",
"Sunrise Pharma",
"Metro Retail Group",
"BlueWave Energy"
};
const char *syms[NUM_STOCKS] = { "NTEC", "GORE", "SPHR", "MRET", "BWVN" };
double base_prices[NUM_STOCKS] = { 42.50, 18.25, 120.00, 35.80, 55.40 };
double vols[NUM_STOCKS] = { 0.025, 0.035, 0.04, 0.02, 0.03 };
int i;
for (i = 0; i < NUM_STOCKS; i++) {
strncpy(market[i].symbol, syms[i], MAX_SYMBOL_LEN - 1);
market[i].symbol[MAX_SYMBOL_LEN - 1] = '\0';
strncpy(market[i].name, names[i], MAX_NAME_LEN - 1);
market[i].name[MAX_NAME_LEN - 1] = '\0';
market[i].price = base_prices[i];
market[i].prev_price = base_prices[i];
market[i].volatility = vols[i];
holdings.shares[i] = 0;
}
}
/* Простой LCG для платформ без arc4random; достаточно для игры. */
static unsigned rng_state;
static void rng_seed(void) {
rng_state = (unsigned)time(NULL) ^ 0x9E3779B9u;
if (rng_state == 0u) rng_state = 1u;
}
static double rng_uniform(void) {
/* xorshift вместо rand() для предсказуемости качества по дням */
rng_state ^= rng_state << 13;
rng_state ^= rng_state >> 17;
rng_state ^= rng_state << 5;
return (rng_state & 0xFFFFFFu) / (double)0x1000000u;
}
static double rng_gaussian_approx(void) {
/* Сумма 12 U(0,1) - 6 ≈ N(0,1) */
double s = 0.0;
int k;
for (k = 0; k < 12; k++) s += rng_uniform();
return s - 6.0;
}
static void advance_day(void) {
int i;
day++;
for (i = 0; i < NUM_STOCKS; i++) {
double change;
market[i].prev_price = market[i].price;
change = rng_gaussian_approx() * market[i].volatility;
market[i].price *= (1.0 + change);
if (market[i].price < 0.01) market[i].price = 0.01;
}
}
static double portfolio_value(void) {
double v = cash;
int i;
for (i = 0; i < NUM_STOCKS; i++)
v += (double)holdings.shares[i] * market[i].price;
return v;
}
static void print_header(void) {
printf("\n");
printf("================================================================\n");
printf(" БИРЖЕВОЙ СИМУЛЯТОР | День %d | Кэш: $%.2f | Всего: $%.2f\n",
day, cash, portfolio_value());
printf("================================================================\n");
}
static void print_quotes(void) {
int i;
printf("\n %-6s %-22s %10s %10s %8s\n", "Тикер", "Название", "Цена", "Было", "Изм.%");
printf(" ------ ---------------------- ---------- ---------- --------\n");
for (i = 0; i < NUM_STOCKS; i++) {
double pct = 0.0;
if (market[i].prev_price > 1e-9)
pct = 100.0 * (market[i].price - market[i].prev_price) / market[i].prev_price;
printf(" %-6s %-22s %10.2f %10.2f %+7.2f%% [у вас: %d шт.]\n",
market[i].symbol, market[i].name, market[i].price,
market[i].prev_price, pct, holdings.shares[i]);
}
}
static void print_help(void) {
printf("\n Команды:\n");
printf(" buy <тикер> <кол-во> — купить\n");
printf(" sell <тикер> <кол-во> — продать\n");
printf(" next / n — следующий торговый день (котировки меняются)\n");
printf(" portfolio / p — сводка портфеля\n");
printf(" help / h — эта справка\n");
printf(" quit / q — выход\n");
}
static int find_stock(const char *sym) {
int i;
for (i = 0; i < NUM_STOCKS; i++)
if (strcmp(market[i].symbol, sym) == 0) return i;
return -1;
}
static int parse_ticker_arg(char *arg, char *out_sym) {
int j = 0;
while (arg[j] && j < MAX_SYMBOL_LEN - 1) {
char c = arg[j];
if (c >= 'a' && c <= 'z') c = (char)(c - 'a' + 'A');
out_sym[j] = c;
j++;
}
out_sym[j] = '\0';
return j > 0 ? 0 : -1;
}
static int buy_shares(const char *sym, int qty) {
int idx = find_stock(sym);
double cost;
if (idx < 0) {
printf(" Неизвестный тикер: %s\n", sym);
return -1;
}
if (qty <= 0) {
printf(" Количество должно быть положительным.\n");
return -1;
}
cost = (double)qty * market[idx].price;
if (cost > cash + 1e-6) {
printf(" Недостаточно средств. Нужно $%.2f, есть $%.2f\n", cost, cash);
return -1;
}
cash -= cost;
holdings.shares[idx] += qty;
printf(" Куплено %d x %s по $%.2f. Итого: $%.2f. Остаток кэша: $%.2f\n",
qty, sym, market[idx].price, cost, cash);
return 0;
}
static int sell_shares(const char *sym, int qty) {
int idx = find_stock(sym);
double proceeds;
if (idx < 0) {
printf(" Неизвестный тикер: %s\n", sym);
return -1;
}
if (qty <= 0) {
printf(" Количество должно быть положительным.\n");
return -1;
}
if (holdings.shares[idx] < qty) {
printf(" У вас только %d акций %s.\n", holdings.shares[idx], sym);
return -1;
}
proceeds = (double)qty * market[idx].price;
cash += proceeds;
holdings.shares[idx] -= qty;
printf(" Продано %d x %s по $%.2f. Выручка: $%.2f. Кэш: $%.2f\n",
qty, sym, market[idx].price, proceeds, cash);
return 0;
}
static void print_portfolio(void) {
int i;
double equity = portfolio_value();
double pnl = equity - STARTING_CASH;
printf("\n --- Портфель ---\n");
printf(" Кэш: $%.2f\n", cash);
printf(" Стоимость акций по рынку:\n");
for (i = 0; i < NUM_STOCKS; i++) {
if (holdings.shares[i] == 0) continue;
printf(" %-6s %4d шт. x $%-8.2f = $%.2f\n",
market[i].symbol, holdings.shares[i], market[i].price,
(double)holdings.shares[i] * market[i].price);
}
printf(" Итого активы: $%.2f\n", equity);
printf(" P/L от старта ($%.0f): $%+.2f (%+.1f%%)\n",
STARTING_CASH, pnl, 100.0 * pnl / STARTING_CASH);
}
static void trim_newline(char *s) {
size_t n = strlen(s);
if (n > 0 && s[n - 1] == '\n') s[n - 1] = '\0';
}
static void game_loop(void) {
char line[256];
char cmd[32];
char arg1[32];
char sym[MAX_SYMBOL_LEN];
int qty;
int running = 1;
rng_seed();
init_market();
cash = STARTING_CASH;
day = 1;
printf("\nДобро пожаловать! У вас $%.0f. Торгуйте, наблюдайте за рынком.\n",
STARTING_CASH);
print_help();
while (running) {
print_header();
print_quotes();
printf("\n> ");
fflush(stdout);
if (!fgets(line, sizeof line, stdin)) break;
trim_newline(line);
if (line[0] == '\0') continue;
cmd[0] = '\0';
arg1[0] = '\0';
qty = 0;
if (sscanf(line, "%31s %31s %d", cmd, arg1, &qty) < 1) continue;
if (strcmp(cmd, "quit") == 0 || strcmp(cmd, "q") == 0) {
print_portfolio();
printf("\n Конец сессии.\n");
running = 0;
} else if (strcmp(cmd, "help") == 0 || strcmp(cmd, "h") == 0) {
print_help();
} else if (strcmp(cmd, "next") == 0 || strcmp(cmd, "n") == 0) {
advance_day();
printf(" Новый день: %d\n", day);
} else if (strcmp(cmd, "portfolio") == 0 || strcmp(cmd, "p") == 0) {
print_portfolio();
} else if (strcmp(cmd, "buy") == 0 || strcmp(cmd, "b") == 0) {
if (parse_ticker_arg(arg1, sym) != 0 || qty <= 0) {
printf(" Использование: buy <ТИКЕР> <кол-во>\n");
continue;
}
(void)buy_shares(sym, qty);
} else if (strcmp(cmd, "sell") == 0 || strcmp(cmd, "s") == 0) {
if (parse_ticker_arg(arg1, sym) != 0 || qty <= 0) {
printf(" Использование: sell <ТИКЕР> <кол-во>\n");
continue;
}
(void)sell_shares(sym, qty);
} else {
printf(" Неизвестная команда. Введите help\n");
}
}
}
int main(void) {
game_loop();
return 0;
}