|
| 1 | +#include <chrono> |
| 2 | +#include <cmath> |
| 3 | +#include <cstdio> |
| 4 | +#include <fstream> |
| 5 | +#include <iostream> |
| 6 | +#include <memory> |
| 7 | +#include <random> |
| 8 | +#include <string> |
| 9 | +#include <cuda_runtime.h> |
| 10 | +#include "base.hpp" |
| 11 | + |
| 12 | +//---------------------------------------------------------------------------- |
| 13 | +// KERNEL find2dMean Calcula a média de cada coluna na GPU |
| 14 | +//---------------------------------------------------------------------------- |
| 15 | +__global__ void find2dMeanKernel(float* d_matrix, float* d_avg, int numLoops, int timeSteps) |
| 16 | +{ |
| 17 | + int col = blockIdx.x * blockDim.x + threadIdx.x; |
| 18 | + |
| 19 | + if (col < timeSteps) |
| 20 | + { |
| 21 | + float sum = 0.0f; |
| 22 | + for (int row = 0; row < numLoops; row++) |
| 23 | + { |
| 24 | + // Acesso à matriz linearizada [linha * total_colunas + coluna] |
| 25 | + sum += d_matrix[row * timeSteps + col]; |
| 26 | + } |
| 27 | + d_avg[col] = sum / numLoops; |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +//---------------------------------------------------------------------------- |
| 32 | +// FUNÇÃO que gerencia os dados da CPU para GPU |
| 33 | +//---------------------------------------------------------------------------- |
| 34 | +float* find2dMeanGPU(float* h_matrix, int numLoops, int timeSteps) |
| 35 | +{ |
| 36 | + size_t matrixSize = numLoops * timeSteps * sizeof(float); |
| 37 | + size_t avgSize = timeSteps * sizeof(float); |
| 38 | + float *d_matrix, *d_avg; |
| 39 | + float* h_avg = new float[timeSteps]; |
| 40 | + |
| 41 | + // 1. Alocar memória na GPU |
| 42 | + cudaMalloc(&d_matrix, matrixSize); |
| 43 | + cudaMalloc(&d_avg, avgSize); |
| 44 | + |
| 45 | + // 2. Copiar dados da CPU para a GPU |
| 46 | + cudaMemcpy(d_matrix, h_matrix, matrixSize, cudaMemcpyHostToDevice); |
| 47 | + |
| 48 | + // 4. Executar o kernel |
| 49 | + int threadsPerBlock = 256; |
| 50 | + int blocksPerGrid = (timeSteps + threadsPerBlock - 1) / threadsPerBlock; |
| 51 | + find2dMeanKernel<<<blocksPerGrid, threadsPerBlock>>>(d_matrix, d_avg, numLoops, timeSteps); |
| 52 | + |
| 53 | + // 5. Copiar o resultado da GPU para a CPU |
| 54 | + cudaMemcpy(h_avg, d_avg, avgSize, cudaMemcpyDeviceToHost); |
| 55 | + |
| 56 | + // 6. Liberar a memória da GPU |
| 57 | + cudaFree(d_matrix); |
| 58 | + cudaFree(d_avg); |
| 59 | + |
| 60 | + return h_avg; |
| 61 | +} |
| 62 | + |
| 63 | + |
| 64 | +//---------------------------------------------------------------------------- |
| 65 | +// Calcula a volatilidade a partir do arquivo data.csv |
| 66 | +//---------------------------------------------------------------------------- |
| 67 | +float calculateVolatility(float spotPrice, int32_t timeSteps) |
| 68 | +{ |
| 69 | + // Abre o arquivo data.csv em modo de leitura, encerra em caso de falha |
| 70 | + std::ifstream filePtr; |
| 71 | + filePtr.open("nvidia_stock.txt", std::ifstream::in); |
| 72 | + |
| 73 | + if (!filePtr.is_open()) |
| 74 | + { |
| 75 | + std::cerr << "Não foi possível abrir o arquivo! Encerrando..\n"; |
| 76 | + exit(EXIT_FAILURE); |
| 77 | + } |
| 78 | + |
| 79 | + int32_t i = 0; |
| 80 | + int32_t maxLen = timeSteps - 1; |
| 81 | + std::unique_ptr<float[]> priceArr = std::make_unique<float[]>(maxLen); |
| 82 | + std::string line; |
| 83 | + |
| 84 | + while( std::getline(filePtr, line) && (i < maxLen) ){ |
| 85 | + if(line.empty()) continue; |
| 86 | + |
| 87 | + try { |
| 88 | + priceArr[i] = std::stof(line); |
| 89 | + i++; |
| 90 | + |
| 91 | + } catch (const std::exception& e){ |
| 92 | + std::cerr << "Erro ao converter o valor da linha " << i <<"\n"; |
| 93 | + continue; |
| 94 | + } |
| 95 | + |
| 96 | + } |
| 97 | + filePtr.close(); |
| 98 | + |
| 99 | + float sum = spotPrice; |
| 100 | + // Encontra a média dos preços estimados no final de cada minuto |
| 101 | + for (i = 0; i < maxLen; i++){ |
| 102 | + sum += priceArr[i]; |
| 103 | + } |
| 104 | + float meanPrice = sum / (maxLen + 1); |
| 105 | + |
| 106 | + // Calcula a volatilidade do mercado como o desvio padrão |
| 107 | + sum = std::pow((spotPrice - meanPrice), 2.0f); |
| 108 | + for (i = 0; i < maxLen; i++){ |
| 109 | + sum += std::pow((priceArr[i] - meanPrice), 2.0f); |
| 110 | + } |
| 111 | + |
| 112 | + float stdDev = std::sqrt(sum / maxLen); // Nota: Fórmula do desvio padrão amostral requer divisão pelo tamanho |
| 113 | + |
| 114 | + // Retorna como porcentagem |
| 115 | + return stdDev / 100.0f; |
| 116 | +} |
| 117 | + |
| 118 | + |
| 119 | +/** --------------------------------------------------------------------------- |
| 120 | + Gera um número aleatório semeado pelo relógio do sistema baseado na |
| 121 | + distribuição normal padrão assumindo média 0.0 e desvio padrão 1.0 |
| 122 | +----------------------------------------------------------------------------*/ |
| 123 | +float genRand(float mean, float stdDev) |
| 124 | +{ |
| 125 | + const auto seed = std::chrono::system_clock::now().time_since_epoch().count(); |
| 126 | + std::default_random_engine generator(static_cast<uint32_t>(seed)); |
| 127 | + std::normal_distribution<float> distribution(mean, stdDev); |
| 128 | + return distribution(generator); |
| 129 | +} |
| 130 | + |
| 131 | +//---------------------------------------------------------------------------- |
| 132 | +// Simula o modelo de Black-Scholes |
| 133 | +//---------------------------------------------------------------------------- |
| 134 | +float* runBlackScholesModel(float spotPrice, int32_t timeSteps, float riskRate, float volatility) |
| 135 | +{ |
| 136 | + // Média e desvio padrão |
| 137 | + static constexpr float mean = 0.0f, stdDev = 1.0f; |
| 138 | + // Intervalo de tempo (Timestep) |
| 139 | + float deltaT = 1.0f / timeSteps; |
| 140 | + std::unique_ptr<float[]> normRand = std::make_unique<float[]>(timeSteps - 1); // Array de números aleatórios distribuídos normalmente |
| 141 | + float* stockPrice = new float[timeSteps]; // Array do preço da ação em diferentes tempos |
| 142 | + stockPrice[0] = spotPrice; // O preço da ação em t=0 é o preço à vista (spot price) |
| 143 | + |
| 144 | + // Preenche o array com números aleatórios |
| 145 | + for (int32_t i = 0; i < timeSteps - 1; i++) |
| 146 | + normRand[i] = genRand(mean, stdDev); |
| 147 | + |
| 148 | + // Aplica a equação de Black-Scholes para calcular o preço da ação no próximo passo de tempo |
| 149 | + for (int32_t i = 0; i < timeSteps - 1; i++) |
| 150 | + stockPrice[i + 1] = stockPrice[i] * exp(((riskRate - (std::pow(volatility, 2.0f) / 2.0f)) * deltaT) + (volatility * normRand[i] * std::sqrt(deltaT))); |
| 151 | + |
| 152 | + return stockPrice; |
| 153 | +} |
| 154 | + |
| 155 | + |
| 156 | +void displayHelp(char* programName) { |
| 157 | + std::cout << "\n========================================================================\n"; |
| 158 | + std::cout << " SIMULADOR DE MONTE CARLO - MODELO BLACK-SCHOLES (NVIDIA PREDICT)\n"; |
| 159 | + std::cout << "========================================================================\n\n"; |
| 160 | + std::cout << "Uso: " << programName << " [opcionais]\n\n"; |
| 161 | + std::cout << "Sintaxe dos Argumentos:\n"; |
| 162 | + std::cout << " 1. [inLoops] Iterações internas para redução de variância (Padrão: 100)\n"; |
| 163 | + std::cout << " 2. [outLoops] Total de simulações de Monte Carlo (Padrão: 10000)\n"; |
| 164 | + std::cout << " 3. [timeStepsHistory] Janela de dados históricos para cálculo de volatilidade (Padrão: 180)\n"; |
| 165 | + std::cout << " 4. [timeStepsForecast] Horizonte de projeção da série temporal (Padrão: 180)\n"; |
| 166 | + std::cout << " 5. [spotPrice] Preço atual do ativo (S_0) no tempo zero (Padrão: 0.50)\n"; |
| 167 | + std::cout << " 6. [riskRate] Taxa livre de risco anualizada (Risk-free rate) (Padrão: 0.5)\n\n"; |
| 168 | + |
| 169 | + std::cout << "Descrição Técnica:\n"; |
| 170 | + std::cout << " Este software realiza uma simulação estocástica baseada no Movimento \n"; |
| 171 | + std::cout << " Browniano Geométrico. Ele processa dados históricos de fechamento para \n"; |
| 172 | + std::cout << " derivar a volatilidade implícita e projeta N caminhos possíveis, \n"; |
| 173 | + std::cout << " consolidando-os em um resultado estatisticamente convergente (opt.csv).\n\n"; |
| 174 | + |
| 175 | + std::cout << "Exemplo de execução avançada:\n"; |
| 176 | + std::cout << " " << programName << " 500 50000 252 30 145.20 0.05\n"; |
| 177 | + std::cout << " (Simula 30 dias com 25 milhões de trajetórias totais)\n"; |
| 178 | + std::cout << "========================================================================\n\n"; |
| 179 | +} |
| 180 | + |
| 181 | + |
| 182 | +/* ============================================================================ |
| 183 | + APLICAÇÃO DO MÉTODO DE MONTE CARLO |
| 184 | + ---------------------------------- |
| 185 | + O Método de Monte Carlo utiliza amostragens aleatórias massivas para modelar |
| 186 | + sistemas complexos (que não possuem uma solução exata simples) e estimar os |
| 187 | + seus resultados através de probabilidade. |
| 188 | +
|
| 189 | + Neste código, ele é aplicado da seguinte forma: |
| 190 | + 1. Aleatoriedade (Caminhos): O modelo gera múltiplos cenários ou "caminhos" |
| 191 | + possíveis para o preço da ação ao longo do tempo usando a equação estocástica |
| 192 | + de Black-Scholes (movimento browniano geométrico), impulsionada pela |
| 193 | + função de números aleatórios 'genRand'. |
| 194 | + 2. Repetição: Executamos milhares dessas simulações independentes através de |
| 195 | + laços de repetição aninhados (controlados por 'inLoops' e 'outLoops'). |
| 196 | + 3. Agregação: Ao final, calculamos a média (find2dMean) de todos esses caminhos |
| 197 | + simulados. Pela Lei dos Grandes Números, a média destas simulações converge |
| 198 | + para o valor esperado ou o "resultado mais provável" do comportamento da |
| 199 | + ação ao longo do tempo em um mercado volátil. |
| 200 | +============================================================================ */ |
| 201 | + |
| 202 | +//---------------------------------------------------------------------------- |
| 203 | +// Função Principal |
| 204 | +//---------------------------------------------------------------------------- |
| 205 | + |
| 206 | + |
| 207 | +int32_t run(int32_t inLoops, int32_t outLoops, int32_t timeStepsHistory, |
| 208 | + int32_t timeStepsForecast, float spotPrice, float riskRate) |
| 209 | +{ |
| 210 | + // Agora usamos memória linear para facilitar a transferência para GPU |
| 211 | + float* h_stockFlat = new float[inLoops * timeStepsForecast]; |
| 212 | + float* h_avgStockFlat = new float[outLoops * timeStepsForecast]; |
| 213 | + |
| 214 | + const float volatility = calculateVolatility(spotPrice, timeStepsHistory); |
| 215 | + |
| 216 | + std::cout << "Usando volatilidade de mercado: " << volatility << "\n"; |
| 217 | + std::cout << "Executando aceleração via GPU (Kernel find2dMean)\n\n"; |
| 218 | + |
| 219 | + for (int32_t i = 0; i < outLoops; i++) |
| 220 | + { |
| 221 | + for (int32_t j = 0; j < inLoops; j++) |
| 222 | + { |
| 223 | + float* path = runBlackScholesModel(spotPrice, timeStepsForecast, riskRate, volatility); |
| 224 | + // Copia o caminho para a matriz linearizada |
| 225 | + for(int k=0; k < timeStepsForecast; k++) { |
| 226 | + h_stockFlat[j * timeStepsForecast + k] = path[k]; |
| 227 | + } |
| 228 | + delete[] path; |
| 229 | + } |
| 230 | + |
| 231 | + // CHAMA A GPU para processar as médias do bloco interno |
| 232 | + float* avgResult = find2dMeanGPU(h_stockFlat, inLoops, timeStepsForecast); |
| 233 | + |
| 234 | + // Armazena na matriz de médias global |
| 235 | + for(int k=0; k < timeStepsForecast; k++) { |
| 236 | + h_avgStockFlat[i * timeStepsForecast + k] = avgResult[k]; |
| 237 | + } |
| 238 | + delete[] avgResult; |
| 239 | + } |
| 240 | + |
| 241 | + // Calcula o resultado final (Média das Médias) também na GPU |
| 242 | + float* optStock = find2dMeanGPU(h_avgStockFlat, outLoops, timeStepsForecast); |
| 243 | + |
| 244 | + // Grava o resultado |
| 245 | + std::ofstream filePtr("opt.csv"); |
| 246 | + if (filePtr.is_open()) { |
| 247 | + for (int32_t i = 0; i < timeStepsForecast; i++) |
| 248 | + filePtr << optStock[i] << "\n"; |
| 249 | + filePtr.close(); |
| 250 | + } |
| 251 | + |
| 252 | + delete[] h_stockFlat; |
| 253 | + delete[] h_avgStockFlat; |
| 254 | + delete[] optStock; |
| 255 | + |
| 256 | + return EXIT_SUCCESS; |
| 257 | +} |
| 258 | + |
| 259 | +int32_t timed_run(int32_t inLoops, int32_t outLoops, int32_t timeStepsHistory, |
| 260 | + int32_t timeStepsForecast, float_t spotPrice, float_t riskRate){ |
| 261 | + const auto beginTime = std::chrono::steady_clock::now(); |
| 262 | + |
| 263 | + if(run(inLoops, outLoops, timeStepsHistory, timeStepsForecast, spotPrice, riskRate)){ |
| 264 | + return -1; |
| 265 | + } |
| 266 | + |
| 267 | + const auto endTime = std::chrono::steady_clock::now(); |
| 268 | + const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - beginTime).count(); |
| 269 | + return elapsed; |
| 270 | +} |
| 271 | + |
| 272 | + |
| 273 | +int main(int argc, char** argv) |
| 274 | +{ |
| 275 | + static int inLoops = 100; // Iterações do laço interno |
| 276 | + static int outLoops = 100; // Iterações do laço externo |
| 277 | + static int timeStepsHistory = 100; // Intervalos de tempo do mercado de ações (dias) |
| 278 | + static int timeStepsForecast = 100; // Intervalos de tempo do mercado de ações (dias) |
| 279 | + static float spotPrice = 0.50f; // Preço à vista (spot price em t = 0) |
| 280 | + static float riskRate = 0.5; // Taxa de juros livre de risco (%) |
| 281 | + |
| 282 | + if ( (argc != 1 ) && ( argc != 7 )){ |
| 283 | + displayHelp(argv[0]); |
| 284 | + return 0; |
| 285 | + } |
| 286 | + |
| 287 | + inLoops = std::stoi(argv[1]); |
| 288 | + outLoops = std::stoi(argv[2]); |
| 289 | + timeStepsHistory = std::stoi(argv[3]); |
| 290 | + timeStepsForecast = std::stoi(argv[4]); |
| 291 | + spotPrice = std::stof(argv[5]); |
| 292 | + riskRate = std::stof(argv[6]); |
| 293 | + |
| 294 | + std::cout << "--- Configurações da Simulação ---" << "\n"; |
| 295 | + std::cout << "Laços Internos: " << inLoops << "\n"; |
| 296 | + std::cout << "Laços Externos: " << outLoops << "\n"; |
| 297 | + std::cout << "Histórico (dias): " << timeStepsHistory << "\n"; |
| 298 | + std::cout << "Previsão (dias): " << timeStepsForecast << "\n"; |
| 299 | + std::cout << "Preço Inicial: " << spotPrice << "\n"; |
| 300 | + std::cout << "Taxa de Risco: " << riskRate << "\n"; |
| 301 | + std::cout << "----------------------------------" << "\n"; |
| 302 | + |
| 303 | + |
| 304 | + int elapsed = timed_run(inLoops, outLoops, timeStepsHistory, timeStepsForecast, spotPrice, riskRate); |
| 305 | + |
| 306 | + std::cout << "Executado em " << elapsed << " s\n"; |
| 307 | + |
| 308 | + |
| 309 | + return 0; |
| 310 | +} |
0 commit comments