SISTEMA PARA AUXÍLIO E GESTÃO DE
INVESTIMENTOS FINANCEIROS
Lucas de Carvalho Frucht
Projeto de Graduação apresentado ao Curso de
Engenharia Eletrônica e de Computação da Escola
Politécnica, Universidade Federal do Rio de
Janeiro, como parte dos requisitos necessários à
obtenção do título de Engenheiro.
Orientador: Sérgio Palma da Justa Medeiros
Rio de Janeiro
Setembro de 2018
iv
UNIVERSIDADE FEDERAL DO RIO DE JANEIRO
Escola Politécnica – Departamento de Eletrônica e de Computação
Centro de Tecnologia, bloco H, sala H-217, Cidade Universitária
Rio de Janeiro – RJ CEP 21949-900
Este exemplar é de propriedade da Universidade Federal do Rio de Janeiro, que
poderá incluí-lo em base de dados, armazenar em computador, microfilmar ou adotar
qualquer forma de arquivamento.
É permitida a menção, reprodução parcial ou integral e a transmissão entre
bibliotecas deste trabalho, sem modificação de seu texto, em qualquer meio que esteja
ou venha a ser fixado, para pesquisa acadêmica, comentários e citações, desde que sem
finalidade comercial e que seja feita a referência bibliográfica completa.
Os conceitos expressos neste trabalho são de responsabilidade do(s) autor(es).
v
DEDICATÓRIA
Neste ano marcante, em que se completaram 130 anos da assinatura da Lei Áurea, que
em 13 de maio de 1988 declarou extinta a escravidão no Brasil, o homem que mais
lutou para que realmente se acabasse a escravidão no Brasil está preso e impedido de
concorrer nas eleições. Durante seu governo a pobreza foi reduzida em mais de 50%,
abrindo caminho para que o país saísse do Mapa da Fome em 2014. Tivemos um
aumento no Índice de Desenvolvimento Humano e uma redução no Coeficiente de Gini,
que mede a desigualdade de renda. As políticas de distribuição de renda foram
unificadas e expandidas. O salário mínimo teve um crescimento real ano após ano e o
desemprego foi reduzido. Hoje ele está preso, condenado em um processo em que não
foi apresentada nenhuma prova fática, em que depoimentos foram ignorados e cuja
competência do foro é questionável. Além disso, ele está sendo impedido de se
candidatar e de fazer campanha ainda que o Comitê de Direitos Humanos da
Organização das Nações Unidas, cujas decisões o Brasil se comprometeu a acatar, tenha
expedido uma liminar requerendo que lhe seja garantido o exercício de seus direitos
políticos.
Dedico este trabalho ao presidente Luiz Inácio Lula da Silva.
vi
AGRADECIMENTO
Agradeço a todos os colegas com quem convivi ao longo do curso, cada um deles
contribuíram de alguma forma na minha formação. Aqueles que me ajudaram a estudar
nas matérias em que tive dificuldade, que me apoiaram sempre que precisei, que me
criticaram quando tomei decisões erradas e que me incentivaram quando não me
dediquei o suficiente, e também os que me deram a oportunidade de ajuda-los. Os
diversos amigos que fiz e que, mesmo que possam estar distantes, estarão sempre no
meu coração. Agradeço especialmente a Adriano Cruz dos Santos Soares, Alan
Carpilovsky, Alexandre Jannuzzi Rios, Allan Bides de Andrade, Allan Freitas da Silva,
André Felipe Suzano Massa, André Luiz Batalha Alcântara, Andre Salviano Calmon,
Beatriz Silva dos Rios, Bernardo Cid Killer Soares de Souza, Bruno Fraga, Camila
Simões da Costa Cunha Vasconcellos, Carlos Pedro Vianna Lordelo, Eduardo Santoro
Morgan, Ewerton Rivero Fragoso, Felipe de Menezes Machado, Felipe Mayer
Gonçalves, Felipe Rembold Petraglia, Felipe Senra Ribeiro, Felippe Kern Noel, Franz
Acker Lobianco, Gabriel Alboretti de Souza, Gabriel Mendes Gouvêa, Hugo Henriques
Gomes de Andrade, Ian Esteves do Nascimento, Igor Oliveira Gameleiro, Igor Paladino
Gomes da Costa, Isabela Ferrão Apolinário, Isadora Kucera Bottino, João Bernardo
Oliveira, Júlia Clara Siqueira Lopez, Leonardo Alvim Muricy, Laís Ferreira Crispino,
Larissa Corrêa Batista Guimarães, Laura Marra Pires, Lucas Arrabal Thomaz, Lucas
Simões Maia, Luciana dos Santos Netto dos Reys, Manoel Fernando de Sousa
Domingues Junior, Marcelo Pita Gomes de Castro, Marina Torres Ferreira de Souza,
Maurício do Vale Madeira da Costa, Natália França Tavares, Oliver von Behr Kuster,
Paulo Roberto Yamasaki Catunda, Pedro Angelo Medeiros Fonini, Pedro Bandeira de
Mello Martins, Pedro Corrêa da Silva, Rafael Lopes Conde dos Reis, Renato Pescarini
Valério, Rick Miranda Ferreira, Silvino Vieira da Silva, Thiago Valentin de Oliveira,
Tiago Bitarelli Gomes e Victor Teixeira Rodrigues. Agradeço também a minha melhor
amiga, que esteve comigo nos períodos finais da faculdade e muito me apoiou ao longo
da elaboração desse trabalho, minha namorada Larissa Drummond Alvarenga.
Agradeço ao meu orientador, o professo Sérgio Palma da Justa Medeiros, que sempre
trás para o nosso curso uma visão moderna e alinhada com a realidade do mercado e
com o que ele espera de nós; ao professor Antônio Cláudio Gómez de Souza, que foi
quem recebeu a mim e a minha turma na nossa primeira aula no curso, que nós ensinou
vi
i
as mais diversas formas de se implementar uma agenda e que sempre se preocupou em
manter nosso horizonte aberto para além dos aspectos puramente técnicos da
engenharia, com quem dei monitória por três períodos e que participa da banca desse
trabalho; ao professor Marcelo Luiz Drumond Lanza, cuja exigência em relação às boas
práticas de programação deixa uma marca indelével, na qualidade dos códigos que
criamos, com quem dei monitoria por dois períodos; ao professor Carlos José Ribas
D'Avila (Casé) que nos diversos anos em que foi coordenador do curso foi sempre
extremamente disponível e atencioso com todos os alunos, nos tratando como gostaria
que tratassem seus filhos; ao professor José Paulo Brafman que além de digerir todas as
especificidades de uma importante linha de microprocessadores e explica-las de uma
forma que pudéssemos compreender facilmente, como chefe de departamento foi
extremamente ábil em gerir nosso limitado orçamento e promover uma modernização
da nossa infraestrutura; ao professor Márcio Nogueira de Souza, meu orientador na
Iniciação Científica; ao professor José Arthur da Rocha, que participa da banca desse
trabalho; ao professor Carlos Fernando Teodósio Soares; ao professor Jomar Gozzi; ao
professor Alexandre Visintainer Pino; ao professor Osvaldo Pereira Filho; ao professor
Luiz Wagner Pereira Biscainho; ao professor Ericksson Rocha e Almendra; e aos
demais professores que participaram da minha formação como Engenheiro Eletrônico e
de Computação. Agradeço também aos meus professores da educação básica na Nossa
Escolinha, na Escola Sá Pereira, no Colégio Pedro II e no Colégio São Vicente de
Paulo, que me ajudaram a construir a base que me permitiu chegar até aqui; em
especial, agradeço a Andréa Nívea, Maria Concetta e Hugo Pinheiro.
Agradeço a todos os funcionários da nossa grande universidade, que são
importantíssimos para o seu funcionamento. Em especial, agradeço a Conceição da
Costa Reis e Luis Alberto de Mello (Luisinho), que sempre cuidam da secretaria do
nosso Departamento de Engenharia Eletrônica e de Computação e das necessidades de
nossos alunos e professores; a Isaías Mendes dos Santos e Marcio Augusto Chaves, que
além de cuidarem da infraestrutura de nosso departamento, auxiliam os alunos com os
materiais usados nas aulas práticas de eletrônica; a Alexandre Herculano Ferreira.
Freitas, Fábio Marcio Miranda e Mariana Fernandes de Mello Sodré, meus
companheiros de Conhecendo a UFRJ, e que sempre estiveram prontos para auxiliar os
alunos em suas atuações na secretária da Escola Politécnica, no gabinete do diretor e na
Diretoria Adjunta de Ensino e Cultura; a Rivera Lisandro Guianze; a Diego Barcellos
do Amaral.
vi
ii
Agradeço aos profissionais com quem atuei ao longo dos meus estágios e que
contribuíram enormemente para minha formação como engenheiro, em especial a
Alexandre Theodoro, Brian Hansen e Marcelo Dante.
Agradeço a minha família que me apoiou e me incentivou ao longo de toda a minha
trajetória, em especial meus pais, José Luciano Janeiro Frucht e Thais Callejo de
Carvalho; meus avós, Maria de Lourdes Janeiro Frucht (In memoriam), Mauro
Nogueira de Carvalho e Wanda Emilia Callejo de Carvalho; meus primos Lorenzo
Sevieri e Mariana Frucht Maia; e meus tios Andreia Maria Janeiro Frucht e Cleiber
Pinheiro Maia, Patricia Callejo de Carvalho e Giampaolo Sevieri e Regina Malia
Janeiro Frucht.
Agradeço por fim ao povo brasileiro, que patrocinou, através dos impostos pagos com o
suor de seus rostos, meus estudos nesta Universidade Federal do Rio de Janeiro, que
tanto amo.
ix
RESUMO
Este estudo mostra alguns conceitos ligados ao mercado de ações e sobre o
imposto de renda aplicado a esse mercado e mostra como é possível criar uma
ferramenta computacional para ajudar em tomadas de decisão ligadas a esse mercado,
oferecendo analises técnicas do mercado e indicadores de desempenho, e para ajudar na
apuração do imposto devido.
Palavras-Chave: mercado de ações, imposto de renda, software, ferramenta de tomada
de decisão.
x
ABSTRACT
This study shows some concepts related to the stock market and about the
income tax that apply on this market and show how it is possible to create a computer
tool to help in the decision making related to this market, offering technical analysis and
performance indicators, and to help in the calculation of the due tax.
Key-words: stock market, income tax, software decision making tool.
xi
SIGLAS
AJAX – Asynchronous JavaScript and XML
API – Application Programming Interface
AWS – Amazon Web Services
B3 – Brasil Bolsa Balcão
CSS – Cascading Style Sheets
ECMA – European Computer Manufacturers Association
HTML – Hypertext Markup Language
HTTP – Hypertext Transfer Protocol
IPCA – Índice Nacional de Preços ao Consumidor Amplo
IR – Imposto de Renda
JAX-RS – Java API for RESTful Web Services
JS - JavaScript
JSON – JavaScript Object Notation
JVM – Java Virtual Machine
REST – Representational State Transfer
SaaS – Software as a Service
SHA – Secure Hash Algorithm
XML – Extensible Markup Language
xi
i
Sumário
1 Introdução 1
1.1 - Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 - Delimitação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3 - Justificativa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.4 - Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.5 - Metodologia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.6 - Descrição . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2 Fundamentações teóricas 4
2.1 - Mercado de Ações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2 - Imposto de Renda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3 Requisitos 7
3.1 - Requisitos Funcionais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.2 - Requisitos Não Funcionais . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4 Casos de uso 10
4.1 - Login (UC01) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
4.2 - Logout (UC02) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.3 - Cadastro (UC03) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.4 - Cadastrar nota (UC04) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.5 - Listar operações (UC05) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
xi
ii
4.6 - Listar posições encerradas (UC06) . . . . . . . . . . . . . . . . . . . . 15
4.7 - Obter série histórica (UC07) . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.8 - Exibir série histórica (UC08) . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.9 - Exibir médias móveis (UC09) . . . . . . . . . . . . . . . . . . . . . . . . 18
4.10 - Exibir candlestick (UC10) . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.11 - Exibir evolução da carteira (UC11) . . . . . . . . . . . . . . . . . . . 19
4.12 - Exibir gráfico comparativo (UC12) . . . . . . . . . . . . . . . . . . . 19
5 Modelo de dados 21
6 Conclusões 22
6.1 - Conclusões do projeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
6.2 - Conclusões acadêmicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
6.3 - Trabalhos Futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Bibliografia 24
A Códigos do back-end (Java) 25
B Códigos do Front-end (HTML/JS/CSS) 45
C Scripts de Banco de Dados 56
D Licença da biblioteca HighCharts 59
xi
v
Lista de Figuras
5.1 – Modelo de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1
Capítulo 1
Introdução
1.1 – Tema
Este trabalho descreve um sistema computacional multiusuário baseado em
computação em nuvem no modelo Software como um Serviço (SaaS). Este sistema visa
auxiliar seus usuários em seus investimentos, fornecendo relatórios gerenciais e fiscais,
bem como auxiliando na tomada de decisão.
1.2 – Delimitação
O público alvo do sistema descrito por este trabalho são pessoas físicas que
realizem investimentos financeiros ou que estejam se planejando para começar a
realizá-los em um futuro próximo. No escopo deste trabalho o sistema abordará apenas
questões ligadas ao investimento em ações, nos mercados à vista e fracionário,
negociadas na Brasil Bolsa Balcão (B3).
1.3 – Justificativa
Educação financeira e previdenciária são assuntos raramente abordados na
formação básica da nossa população. Isso faz com que uma grande massa gaste mais do
que ganha, passando boa parte da vida endividada, além de não entender a importância
de guardar dinheiro para uma eventualidade ou para a velhice. Além disso, a falta de
informação faz com que a maioria dos que poupam acabem preferindo investimentos
considerados mais simples e de pouco retorno, como a caderneta de poupança e os
planos de previdência e de capitalização.
2
Neste contexto, torna-se desejável uma ferramenta que possa auxiliar esse
pequeno investidor ou futuro investidor com análises técnicas que indiquem
investimentos com maiores perspectivas de rendimento, além de análises de
desempenho que mostrem se suas escolhas foram realmente vantajosas em comparação
com investimentos mais tradicionais.
Além disso, outro ponto que desencoraja muitos pequenos investidores,
especificamente no caso dos investimentos em bolsas de mercadorias e de valores, são
as dificuldades para se declarar esses investimentos e seus ganhos à Receita Federal e de
pagar o imposto devido, o que deve ser feito mensalmente, visto que a Receita ainda
não disponibiliza uma ferramenta que auxilie na apuração dos ganhos e no cálculo do
imposto, diferentemente do que acontece com outras receitas no aplicativo de
declaração de ajuste anual e do carnê-leão. Fica totalmente a cargo do investidor, caso
não deseje contratar um contador, entender a metodologia do cálculo, levantar as
informações e calcular o imposto devido. Desta forma, outra funcionalidade desejável é
a apuração dos rendimentos e do imposto devido das operações com ações.
1.4 – Objetivos
O objetivo deste trabalho é descrever e implementar uma ferramenta
computacional com as seguintes funcionalidades, que auxiliarão nos problemas citados
no item anterior: ferramentas de tomada de decisão, baseadas em análises técnicas do
mercado de ações, que auxiliem o investidor iniciante a encontrar uma ação com maior
potencial de retorno, como os gráficos de médias móveis e de candlesticks; relatórios
gerenciais que indiquem, através de gráficos comparativos, a performance da carteira
montada pelo investidor e comparem essa performance com indicadores de inflação e
com a performance que o mesmo valor investido teria em outros investimentos, como a
poupança; relatórios fiscais indicando o quanto deve ser recolhido de imposto de renda
mensalmente e o que deve ser preenchido na declaração de ajuste anual, tais relatórios
exibiriam as vendas realizadas no mês, o valor do imposto de renda relativo àquela
venda, considerando se ele deverá ou não ser pago com base no limite mensal de
isenção.
3
1.5 – Metodologia
O sistema está descrito pelos seus requisitos funcionais e não funcionais e pelos
casos de uso que se desdobraram dos requisitos funcionais. Foi adotada uma segregação
total entre o front-end e o back-end, o que permite diminuir a carga de processamento
no servidor, transfereindo totalmente o processamento da interface para o cliente,
diminuir o tempo de resposta do sistema, por diminuir o volume de dados transmitidos,
diminuindo assim o tempo de rede, que costuma ser o mais custoso, além de permitir
que o sistema seja facilmente encapsulado como um aplicativo móvel, se desejado.
O front-end do sistema, responsável por toda interação do usuário com o
sistema, bem como, pela apresentação dos dados, é totalmente processado nas máquinas
clientes. Ele foi implementado usando as linguagens HTML, JavaScript e CSS. Para a
apresentação de gráficos foi usada a biblioteca JavaScript HighCharts, que oferece uma
gama de ferramentas específicas para a geração de gráficos relacionados a
investimentos financeiros.
O back-end do sistema, responsável pelo armazenamento e processamento dos
dados foi implementado na linguagem Java, usando ferramentas da plataforma Java
Platform, Enterprise Edition (Java EE) para a criação de sistemas web. Foram usadas
também as bibliotecas JAX-RS para a criação de serviços REST e JSON in Java para a
codificação e decodificação de dados em JSON.
A comunicação entre o front-end e o back-end se dá por requisições HTTP
AJAX usando o modelo REST. Essas requisições enviam dados do cliente para o
servidor codificados no formato multpart/form-data. O servidor responde essas
requisições enviando dados codificados no formato JSON.
1.6 – Descrição
O capítulo 2 trará alguns conceitos do domínio do problema, como alguns
fundamentos do mercado de ações, bem como as regras tributárias que se aplicam a esse
mercado. Nos capítulos 3 e 4 são descritos os requisitos e os casos de uso do sistema
respectivamente. No capítulo 5 está a modelagem do banco de dados do sistema. Por
fim, no capítulo 6 são apresentadas as conclusões.
4
Capítulo 2
Fundamentações teóricas
2.1 – Mercado de ações
Empresa
O primeiro conceito que se deve explorar para entender o mercado de ações é o
de empresa, ou, mais formalmente, o de uma sociedade empresária. O Código Civil
brasileiro (instituído pela lei Nº 10.406 de 10 de janeiro de 2002) define empresário
como sendo “quem exerce profissionalmente atividade econômica organizada para a
produção ou a circulação de bens ou de serviços”[10]. Em seguida o mesmo código
diferencia uma empresa individual, que “será constituída por uma única pessoa titular
da totalidade do capital social”[10] de uma sociedade; “celebram contrato de sociedade as
pessoas que reciprocamente se obrigam a contribuir, com bens ou serviços, para o
exercício de atividade econômica e a partilha, entre si, dos resultados”[10]. Chega-se,
então, ao conceito de sociedade empresária, que é “a sociedade que tem por objeto o
exercício de atividade própria de empresário”[10]. Pode-se concluir que a sociedade
empresária é um conjunto de pessoas que reciprocamente se obrigam a contribuir, com
bens ou serviços, para o exercício de atividade econômica organizada para a produção
ou a circulação de bens ou de serviços e a partilhar entre si os resultados dessa atividade
econômica.
Natureza jurídica das empresas
O citado código impõe que “A sociedade empresária deve constituir-se segundo
um dos tipos regulados”[10] e define os diversos tipos regulados, que constituem as
naturezas jurídicas das sociedades empresárias, sendo as mais comuns a sociedade
limitada e a sociedade anônima. Diferentemente da sociedade limitada e de outras que
tem suas regras baseadas na sociedade simples, a sociedade anônima “rege-se por lei
especial”[10] e nela não há a necessidade de os sócios estarem nomeados no contrato
social da sociedade. Isso faz com que a participação em sociedades anônimas possa ser
5
negociada com muito mais agilidade e menos burocracia. A lei 6.404 de 15 de
dezembro de 1976, que é a lei que rege as sociedades anônimas divide ainda essas
sociedades em “aberta ou fechada conforme os valores mobiliários de sua emissão
estejam ou não admitidos à negociação no mercado de valores mobiliários”[11].
A ação
A já citada lei 6.404 define que “A companhia ou sociedade anônima terá o
capital dividido em ações”[11]. Mankiw define que as ações “representam propriedade da
empresa e, portanto, um direito sobre os lucros que a empresa obtiver”[1]. Dessa forma,
sempre que a sociedade anônima distribui seus lucros, ela o faz entre seus sócios, ou
seja, os detentores de ações e o faz de maneira proporcional a quantidade de ações
controladas por cada sócio.
Bolsa de valores
As bolsas de valores são os locais onde são negociadas ações das sociedades
anônimas de capital aberto. Segundo Mankiw “depois que uma empresa emite ações e
as vende ao público, elas são negociadas entre os acionistas em bolsas de valores
organizadas. Nessas transações, a empresa em si não recebe nada quando suas ações
mudam de mãos”[1]. Como se pode ver, as ações são negociadas entre os sócios, que
têm liberdade para definir os preços que estão dispostos a pagar/receber, o que faz com
que os valores dessas ações variem segundo as leis de oferta e procura. Essas
negociações, porém, são intermediadas por corretoras e pela própria bolsa, que cobram
taxas por esses serviços.
Carteira
Chama-se de carteira o conjunto das ações de diversas empresas que uma pessoa
possui.
2.2 – Imposto de Renda
O Imposto de Renda “é um tributo cobrado anualmente pelo governo federal
sobre os ganhos de pessoas e de empresas. Seu valor é pago de acordo com os
rendimentos declarados”[12]. Esse tipo de imposto, que é cobrado na maioria dos países
e há muitos anos, foi instituído no Brasil em 1922. “A história do Imposto de Renda
remonta ao século 18, na Inglaterra. Em 1799, o país precisava angariar recursos para
bancar sua guerra contra a França de Napoleão”[12].
6
Declaração de ajuste anual
Anualmente todo contribuinte que atender certos critérios deve preencher e
entregar a Receita Federal a declaração de ajuste anual, onde devem ser informados
todos os rendimentos, tributáveis ou isentos, além de todos os bens, direitos, dividas e
obrigações. A partir dessa declaração será calculado o valor total de imposto devido
pelo contribuinte naquele ano e comparado com os valores já pagos.
Imposto de Renda sobre lucro com ações
A alíquota de imposto de renda sobre o lucro apurado na venda de ações é de
15%, exceto em operações de day-trade, em que a alíquota é de 20%. Para calcular o
lucro, deve-se considerar a diferença entre o valor de compra e o de venda, e ainda
descontar os custos da operação, como as taxas de liquidação e de corretagem e os
emolumentos. O imposto devido deve ser calculado e pago mensalmente, mas as
operações normais se tornam isentas nos meses em que o valor total das vendas for
menor que 20 mil reais.
7
Capítulo 3
Requisitos
3.1 – Requisitos Funcionais
RF01
O sistema deve exigir que o usuário se autentique antes de realizar qualquer
outra funcionalidade.
RF02
O sistema deve permitir que o usuário se cadastre fornecendo apenas um
endereço de e-mail válido. Neste caso, será gerada uma senha temporária com validade
de 7 dias.
RF03
O sistema deve gerar uma nova senha temporária, com validade de 30 minutos
sempre que o usuário solicitar, caso não consiga se autenticar com sua senha cadastrada.
RF04
Caso o usuário se autentique com uma senha temporária, o sistema deve exigir
que o usuário cadastre uma nova senha.
RF05
O sistema deve permitir que o usuário cadastre notas de corretagem informando
a data, as ações compradas e/ou vendidas, seus respectivos preços, quantidades, custo de
operação por operação e/ou por nota.
RF06
O sistema deve listar as operações cadastradas, incluindo data, quantidade,
preços, custos e posição final naquele ativo.
RF07
O sistema deverá listar as posições encerradas, indicando ação, preço médio de
compra e de venda, taxas de compra e de venda, valor de IR (ou o quanto seria cobrado,
8
caso o limite de isenção tivesse sido ultrapassado no mês), lucro bruto, lucro líquido de
taxas e líquido de taxas e IR.
RF08
O sistema deverá obter diariamente, após o fechamento das negociações, os
preços e volumes de cada ação na B3.
RF09
O sistema deve exibir um gráfico com os valores de fechamento diários para
qualquer ação escolhida pelo usuário no período escolhido, indicando nas datas que o
usuário negociou aquele papel o valor pago e/ou recebido. O sistema também deve
permitir sobrepor a esse gráfico, gráficos de médias móveis e de candlestick da ação
selecionada.
RF10
O sistema deve exibir um gráfico com a evolução diária do valor em carteira do
usuário, considerando o preço de fechamento das ações que o usuário possuía em
carteira no fechamento do pregão naquele dia.
RF11
O sistema deve exibir um gráfico comparando os ganhos e perdas mensais do
usuário com o índice IPCA e com o rendimento da poupança.
3.1 – Requisitos não funcionais
RNF01
O software deve ser disponibilizado na nuvem no modelo Software como um
Serviço.
RNF02
O software deve funcionar em qualquer navegador web compatível com
HTML5, CSS3 e ECMAScript 6, incluindo os de dispositivos móveis.
RNF03
O back-end do software deve poder ser hospedado em qualquer servidor que
possua uma Máquina Virtual Java (JVM) e um servlet container.
RNF04
A persistência dos dados deve ser realizada através de um banco de dados
PostgreSQL.
9
RNF05
O sistema só deve armazenar as senhas dos usuários criptografadas na forma de
um hash, usando o padrão SHA-256.
10
Capítulo 4
Casos de uso
A partir dos requisitos expostos no capítulo anterior, são definidos os seguintes casos de
uso, que balizaram o desenvolvimento do sistema:
4.1 – Login (UC01)
Objetivo: Verificar qual é o usuário que deseja utilizar o sistema e iniciar uma
sessão para esse usuário.
Requisitos relacionados: RF01, RF02, RF03, RF04
Prioridade: Alta
Criticidade: Alta
Freqüência de uso: Alta
Atores: Usuário
Pré-condição: Nenhum usuário deve estar autenticado no sistema na sessão
daquele navegador.
Gatilho: O usuário tenta acessar alguma página do sistema sem estar
autenticado.
Fluxo principal:
P1: O sistema solicita que o usuário informe seu e-mail.
P2: O usuário informa seu e-mail.
P3: O sistema verifica se o e-mail informado é de um usuário cadastrado.
[V1, E1]
P4: O sistema verifica se esse usuário tem uma senha cadastrada ou uma
senha temporária válida. [V2, E1]
P5: O sistema solicita que o usuário informe sua senha.
P6: O usuário informa sua senha.
P7: O sistema verifica se a o hash da senha informada corresponde ao hash
de senha armazenado para aquele usuário. [V3, E1]
11
P8: O sistema invalida todas as senhas temporárias válidas daquele usuário,
alterando a validade para o instante atual.
P9: O sistema cria uma sessão, indicando que o usuário está autenticado.
P10: O sistema exibe sua interface principal.
Fluxo alternativo:
Fluxo variante:
V1: O e-mail informado não é de um usuário cadastrado:
V1a: Uma mensagem é exibida para o usuário informando que o e-mail
ainda não está cadastrado e são oferecidas opções para que ele se cadastre,
ou retorne para tentar com outro e-mail.
V1b: O usuário indica que deseja retornar. [V4]
V1c: O fluxo principal é retomado, no passo P1.
V2: O e-mail informado é de um usuário cadastrado, mas esse usuário não
tem uma senha cadastrada nem uma senha temporária válida:
V2a: Uma nova senha temporária aleatória é gerada com validade de 7
dias.
V2b: O hash e a validade da senha temporária são armazenados.
V2c: A senha temporária é enviada para o e-mail do usuário.
V2d: O fluxo principal é retomado no passo P5.
V3: O hash da senha informada não corresponde ao armazenado.
V3a: O sistema verifica se o hash da senha informada corresponde a um
dos hashs de senha temporária válida daquele usuário. [V5]
V3b: O sistema solicita que o usuário crie uma nova senha.
V3c: O usuário informa sua nova senha.
V3d: O sistema armazena o hash da nova senha informada.
V3d: O fluxo principal é retomado no passo P8
V4: O usuário indica que quer se cadastrar:
V4a: Ponto de extensão: UC03 - Cadastro.
V4b: O caso de uso é retomado no passo P5.
V5: O hash da senha informada não corresponde a nenhum dos hashs de
senhas temporárias:
V5a: Uma mensagem é exibida para o usuário informado que a senha está
incorreta e são oferecidas opções para ele gerar uma nova senha
temporária ou tentar novamente com outra senha.
12
V5b: O usuário indica que deseja tentar novamente com outra senha. [V6].
V5c: O fluxo principal é retomado no passo P5.
V6: O usuário indica que deseja gerar uma nova senha temporária:
V6a: Uma nova senha temporária aleatória é gerada com validade de 30
minutos
V6b: O hash e a validade da senha temporária são armazenados.
V6c: A senha temporária é enviada para o e-mail do usuário.
V6d: O fluxo principal é retomado no passo P5.
Fluxo de exceção:
E1: O sistema não consegue realizar a verificação por falha na comunicação
entre o front-end e o back-end, ou por falha no acesso ao banco de dados.
E1a: Uma mensagem de erro é exibida ao usuário, informando que ele
deve tentar novamente mais tarde.
E1b: O caso de uso é encerrado.
Pós-condição: O usuário está autenticado no sistema.
4.2 – Logout (UC02)
Objetivo: Encerrar a sessão do usuário.
Requisitos relacionados: RF01
Prioridade: Alta
Criticidade: Alta
Freqüência de uso: Média
Atores: Usuário
Pré-condição: Um usuário deve estar autenticado no sistema na sessão daquele
navegador.
Gatilho: O usuário seleciona a opção de “Sair”.
Fluxo principal:
P1: O sistema invalida a sessão.
P2: O sistema exibe a interface de login.
Pós-condição: O usuário não está mais autenticado no sistema.
13
4.3 – Cadastro (UC03)
Objetivo: Cadastrar um novo usuário no sistema.
Requisitos relacionados: RF02
Prioridade: Alta
Criticidade: Média
Freqüência de uso: Baixa
Atores: Usuário
Pré-condição: Nenhum usuário deve estar autenticado no sistema na sessão
daquele navegador; o usuário informou um e-mail não cadastrado na interface de login.
Gatilho: O usuário seleciona a opção de se cadastrar.
Fluxo principal:
P1: O sistema gera uma senha temporária aleatória com validade de 7 dias.
P2: O sistema armazena o e-mail do usuário. [E1]
P3: O sistema cadastra o hash e a validade da senha temporária. [E1]
P3: O sistema envia a senha gerada para o e-mail fornecido pelo usuário. [E2]
Pós-condição: O usuário está cadastrado no sistema.
4.4 – Cadastrar nota (UC04)
Objetivo: Armazenar os dados de uma nota de corretagem e das operações
referentes a ela.
Requisitos relacionados: RF05
Prioridade: Alta
Criticidade: Média
Freqüencia de uso: Média
Atores: Usuário
Pré-condição: O usuário está autenticado.
Gatilho: O usuário seleciona a opção de cadastrar nova nota.
Fluxo principal:
P1: O sistema solicita que o usuário informe a data da nota e, opcionalmente,
os custos relativos àquela nota.
P2: O usuário informa os dados solicitados.
14
P3: O sistema solicita que o usuário informe o código da ação negociada, o
tipo de negócio (compra ou venda) a quantidade negociada, o valor por ação
e, opcionalmente, os custos relativos àquela operação.
P4: O usuário informa os dados solicitados.
P5: O sistema solicita que o usuário informe os mesmos dados do passo P3
para uma nova operação ou selecione para salvar a nota.
P6: O usuário indica que quer salvar a nota. [V1]
P7: O sistema salva os dados informados.
P8: O sistema exibe sua interface principal.
Fluxo alternativo:
Fluxo variante:
V1: O usuário informa os dados referentes a uma nova operação.
V1a: O fluxo principal é retomado no passo P5.
Pós-condição: Os dados da nota a das respectivas operações estão armazenados.
4.5 – Listar operações (UC05)
Objetivo: Listar todas as operações cadastradas pelo usuário.
Requisitos relacionados: RF06
Prioridade: Média
Criticidade: Média
Freqüência de uso: Alta
Atores: Usuário
Pré-condição: O usuário estar autenticado; existirem notas cadastradas.
Gatilho: O usuário seleciona a opção de listar operações.
Fluxo principal:
P1: O sistema recupera data, código da ação, tipo de operação (compra ou
venda), quantidade, valor unitário e custos de operação de cada operação
realizada pelo usuário.
P2: O sistema recupera o custo da nota e calcula a proporção deste custo
referente a cada operação recuperada no passo P1.
P3: O sistema recupera as operações anteriores com as mesmas ações e
calcula a posição final para cada operação recuperada no passo P1.
15
P4: O sistema exibe os dados recuperados para cada operação na forma de
uma tabela.
Pós-condição: N/A
4.6 – Listar posições encerradas (UC06)
Objetivo: Listas todas as posições encerradas, seja em operações normais ou
operações day-trade.
Requisitos relacionados: RF07
Prioridade: Média
Criticidade: Média
Freqûência de uso: Baixa
Atores: Usuário
Pré-condição: O usuário estar autenticado; existirem notas cadastradas.
Gatilho: O usuário seleciona a opção de listar operações.
Fluxo principal:
P1: O sistema recupera as respectivas operações de compra e venda para
operações de day-trade.
P2: O sistema calcula o lucro bruto de cada uma dessas operações, as taxas
totais, o lucro descontado das taxas, o IR incidente e o lucro líquido da
operação.
P3: O sistema recupera as respectivas operações de compra e venda para
negócios concluídos, excluindo as operações day-trade obtidas no passo P1.
P4: O sistema calcula o lucro bruto de cada uma dessas operações, as taxas
totais, o lucro descontado das taxas, o IR incidente e o lucro líquido da
operação.
P5: O sistema exibe os dados recuperados na forma de uma tabela.
Pós-condição: N/A
4.7 – Obter série histórica (UC07)
16
Objetivo: Obter a partir de arquivo disponibilizado pela B3 os preços e volumes
de todos os ativos em uma determinada data.
Requisitos relacionados: RF08
Prioridade: Alta
Criticidade: Alta
Freqüência de uso: Alta
Atores: Site da B3.
Pré-condição: O arquivo com os dados ter sido disponibilizado pela B3
segundo o caminho padrão.
Gatilho: A tarefa é disparada pelo agendador (diariamente às 22h).
Fluxo principal:
P1: O sistema acessa o arquivo no site da B3. [E1]
P2: O sistema descompacta o arquivo e armazena os dados em um buffer.
P3: O sistema verifica se ainda há linhas para serem processadas no buffer.
[V1]
P4: Havendo o sistema analisa a linha seguinte, verificando se ela descreve
uma ação do mercado à vista ou fracionário. [V2]
P5: Caso corresponda a esses mercados, o sistema verifica se a empresa
emissora daquela ação já está cadastrada no sistema. [V3]
P6: Estando cadastrada, o sistema verifica se os dados da ação já estão
cadastrados no sistema. [V4]
P7: Caso já estejam o sistema armazenas os dados relativos a preços e
volumes de negociação daquela ação naquele dia. [E2]
P8: O caso de uso retorna para o passo P3.
Fluxo alternativo:
Fluxo variante:
V1: Não há mais linha para ser processada:
V1a: O caso de uso e a tarefa são encerrados indicado sucesso.
V2: A linha não descreve nenhum dos dois mercados:
V2a: o caso de uso retorna para o passo P3.
V3: A empresa ainda não está cadastrada:
V3a: O sistema registra a empresa com seu nome e código ISIN. [E2]
V3b: O caso de uso retorna para o passo P6
V4: Os dados da ação não estão cadastrados:
17
V4a: O sistema registra a ação com seu código, empresa emissora,
mercado em que a ação é negociada (à vista ou fracionário), a
especificação (PN, ON etc) e o fator de preço (ação unitária ou lote de
1000). [E2]
V4b: O caso de uso retorna para o passo P7
Fluxo de exceção:
E1: O sistema não consegue se comunicar com o site da B3:
E1a: O caso de uso e a tarefa são encerrados indicado fracasso.
E2: O cadastro não pode ser realizado por falha no acesso ao banco de dados:
E2a: O caso de uso e a tarefa são encerrados indicado fracasso.
4.8 – Exibir série histórica (UC08)
Objetivo: Exibir a variação de preços de um ativo destacando pontos de compra
e venda do mesmo.
Requisitos relacionados: RF09
Prioridade: Média
Criticidade: Baixa
Freqüência de uso: Média
Atores: Usuário
Pré-condição: O usuário estar autenticado; haver séries históricas de preços
cadastradas.
Gatilho: O usuário seleciona a opção de exibir série histórica.
Fluxo principal:
P1: O sistema pede que o usuário selecione o ativo.
P2: O usuário informa o ativo.
P3: O sistema pede que o usuário informe o período.
P4: O usuário informa o período.
P5: O sistema recupera todos os preços do ativo selecionado no período
selecionado, bem como todas as operações de compra e venda daquele ativo
realizadas pelo usuário.
Pós-condição: N/A
18
4.9 – Exibir médias móveis (UC09)
Objetivo: Exibir gráficos de médias móveis de curto e longo prazo para auxiliar
o usuário em decisões de compra e/ou venda de um ativo.
Requisitos relacionados: RF09
Prioridade: Baixa
Criticidade: Baixa
Freqüência de uso: Média
Atores: Usuário
Pré-condições: O usuário estar visualizando a série histórica de um ativo.
Gatilho: O usuário ativa a opção de médias móveis.
Fluxo principal:
P1: O sistema pede que o usuário informe o período da média de curto prazo
(média móvel simples)
P2: O usuário informa o período.
P3: O sistema pede que o usuário informe o período de média de longo prazo
(média móvel exponencial).
P4: O usuário informa o período.
P5: O sistema calcula as médias para o período que está sendo exibido no
gráfico.
P6: O sistema acrescenta as curvas ao gráfico.
Pós-condição: N/A
4.10 – Exibir candlestick (UC10)
Objetivo: Exibir gráfico de candlestick para auxiliar o usuário em decisões de
compra e/ou venda de um ativo.
Requisitos relacionados: RF09
Prioridade: Baixa
Criticidade: Baixa
Freqüência de uso: Média
Atores: Usuário
19
Pré-condições: O usuário estar visualizando a série histórica de um ativo.
Gatilho: O usuário ativa a opção de candlestick.
Fluxo principal:
P1: O sistema recupera os preços médio, mínimo e máximo para o período
que está sendo exibido no gráfico.
P2: O sistema acrescenta as curvas ao gráfico.
Pós-condição: N/A
4.11 – Exibir evolução da carteira (UC11)
Objetivo: Exibir gráfico com a evolução da carteira para o usuário ter
informações sobre seu desempenho.
Requisitos relacionados: RF10
Prioridade: Baixa
Criticidade: Baixa
Freqüência de uso: Média
Atores: Usuário
Pré-condições: O usuário estar autenticado; existirem notas cadastradas.
Gatilho: O usuário seleciona a opção de exibir evolução da carteira.
Fluxo principal:
P1: O sistema pede que o usuário informe o período.
P2: O usuário informa o período.
P3: Para cada dia nesse período, o sistema recupera a posição acumulada do
usuário por ativo e o preço de fechamento de cada um desses ativos.
P4: O sistema multiplica o preço de fechamento pela posição acumulada, e
soma os resultados, agrupando-os por dia.
P5: O sistema exibe o resultado na forma de um gráfico.
Pós-condição: N/A
4.12 – Exibir gráfico comparativo (UC12)
20
Objetivo: Exibir gráfico com a evolução da carteira comparada com a evolução
de indicadores para o usuário ter informações sobre seu desempenho.
Requisitos relacionados: RF10
Prioridade: Baixa
Criticidade: Baixa
Freqüência de uso: Média
Atores: Usuário
Pré-condições: O usuário estar autenticado; existirem notas cadastradas.
Gatilho: O usuário seleciona a opção de exibir evolução da carteira.
Fluxo principal:
P1: O sistema pede que o usuário informe o período.
P2: O usuário informa o período.
P3: Para cada dia nesse período, o sistema recupera a posição acumulada do
usuário por ativo e o preço de fechamento de cada um desses ativos.
P4: O sistema multiplica o preço de fechamento pela posição acumulada, e
soma os resultados, agrupando-os por dia, considerando apenas a variação do
preço dos ativos, desconsiderando os preços de compra e venda.
P5: O sistema recupera, para o mesmo período a variação da poupança e do
IPCA.
P6: O sistema exibe o resultado na forma de um gráfico.
Pós-condição: N/A
21
Capítulo 5
Modelo de dados
Para atender os requisitos dos sistema, da forma que foi exposta nos casos de
uso do capítulo anterior, foi definido o modelo de dados da figura 5.1.
Figura 5.1 – Modelo de dados.
22
Capítulo 6
Conclusões
6.1 – Conclusões do projeto
Seguindo as diretrizes expostas nos capítulos desse trabalho, o sistema pode ser
implementado, sendo disponibilizado para apresentação e testes em um servidor
dedicado virtual, com sistema operacional FreeBSD, executando um servlet contêiner
Apache Tomcat, dentro da infraestrutura de computação em nuvem AWS da Amazon.
O sistema criado ainda é bastante limitado, por estar restrito a um tipo de
investimento, mas esta foi a proposta desse trabalho e algumas sugestões de expansão
estão expostas adiante.
6.2 – Conclusões Acadêmicas
Usando os conhecimentos adquiridos nas diversas matérias do curso de
Engenharia Eletrônica e de Computação foi possível elaborar, projetar e construir um
sistema que atende uma necessidade real.
Ao longo do desenvolvimento do projeto foi propiciado o contato com diversas
ferramentas diferentes, que foram úteis em situações diversas do planejamento e da
execução do sistema, como a geração de modelos e a montagem de ambientes de
desenvolvimento e execução do sistema.
6.3 – Projetos Futuros
Para trabalhos futuros poder-se-ia trabalhar em formas de agilizar a entrada dos
dados, como a possibilidade de cadastro de fórmulas para o cálculo de taxas que sejam
padronizadas ou o reconhecimento automático de arquivos com as informações da nota
que fossem gerados pelas diferentes corretoras.
23
Outra abordagem de expansão para o sistema descrito neste trabalho seria a
introdução de métricas e indicadores para avaliar o desempenho, bem como, auxiliar na
tomada de decisão, com foco em outras formas de investimento, como títulos de dívida
públicos e privados e bolsas de mercadorias e futuros.
24
Bibliografia
[1] MANKIW, N. G., Introdução à Economia. São Paulo, Cengage Learning, 2014.
[2] ELMASRI, R., NAVATHE, S.B, Sistemas de Banco de Dados. São Paulo, Pearson,
2010.
[3] PFLEEGER,S. L., Engenharia de Software - Teoria e Prática. São Paulo, Prentice
Hall, 2003.
[4] __________, “Java Platform Standard Edition 8 Documentation”,
https://docs.oracle.com/javase/8/docs/index.html, 2018, (Acesso em 22 Agosto
2018).
[5] __________, “Java(TM) EE 7 Specification APIs”,
https://docs.oracle.com/javaee/7/api/toc.htm, 2015, (Acesso em 22 Agosto 2018).
[6] __________, “Jersey 2.17 API Documentation”,
http://javadox.com/org.glassfish.jersey.media/jersey-media-
multipart/2.17/overview-summary.html, 2015, (Acesso em 22 Agosto 2018).
[7] The PostgreSQL Global Development Group, “PostgreSQL 9.5.14 Documentation”,
https://www.postgresql.org/docs/9.5/static/index.html, 2018, (Acesso em 22 Agosto
2018).
[8] __________, “Highcharts, Highstock and Highmaps documentation”,
https://www.highcharts.com/docs, 2018, (Acesso em 22 Agosto 2018).
[9] __________, “Highcharts JS Options Reference”,
https://api.highcharts.com/highcharts/, 2018, (Acesso em 22 Agosto 2018).
[10] BRASIL, “Lei Nº 10.406, de 10 de Janeiro de 2002”, Institui o Código Civil,
Brasília, 2002.
[11] BRASIL, “Lei Nº 6.404, de 15 de Dezembro de 1976”, Dispõe sobre as Sociedades
por Ações, Brasília, 2076.
[12] __________, “Tudo sobre Imposto de Renda (IR): o que é, como funciona e como
declarar”, https://www.btgpactualdigital.com/blog/imposto/tudo-sobre-imposto-de-
renda, 2017, (Acesso em 30 Agosto 2018).
[13] ROCHA, A., “Guia para cálculo de IR na venda de ações”,
https://www.valor.com.br/valor-investe/o-estrategista/1121864/guia-para-calculo-
de-ir-na-venda-de-acoes, 2011, (Acesso em 30 Agosto 2018).
25
Apêndice A
Códigos do back-end (Java)
A.1 – Arquivo: B3CompanyDao.java
/*
* Universidade Federal do Rio de Janeiro
* Escola Politécnica
*
* Este código faz parte do projeto de graduação apresentado por Lucas
de Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
*
* Rio de Janeiro - Set/2018
*/
package br.com.ldev.minervastocks.dao;
import static br.com.ldev.minervastocks.dao.BaseDao.getConnection;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
*
* @author Administrador
*/
public class B3CompanyDao extends BaseDao {
public static boolean isRegistered(String isin) throws
SQLException, ClassNotFoundException {
Connection conn = getConnection();
boolean result;
PreparedStatement stmt = conn.prepareStatement("SELECT 1
FROM stocks.tb_b3_company WHERE lower(isin_code) = lower(?)");
stmt.setString(1, isin);
result = stmt.executeQuery().next();
conn.close();
return result;
}
public static void register(String name, String isin) throws
SQLException, ClassNotFoundException {
Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement("INSERT
INTO stocks.tb_b3_company(name, isin_code) VALUES(?, ?)");
stmt.setString(1, name);
stmt.setString(2, isin);
stmt.executeUpdate();
26
conn.close();
}
}
A.2 – Arquivo: B3PriceDao.java
/*
* Universidade Federal do Rio de Janeiro
* Escola Politécnica
*
* Este código faz parte do projeto de graduação apresentado por Lucas
de Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
*
* Rio de Janeiro - Set/2018
*/
package br.com.ldev.minervastocks.dao;
import static br.com.ldev.minervastocks.dao.BaseDao.getConnection;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import org.json.JSONArray;
/**
*
* @author Administrador
*/
public class B3PriceDao extends BaseDao {
public static void add(String code, Date date, int openingPrice,
int maxPrice, int minPrice, int avgPrice, int lastPrice, int
bestBuyPrice, int bestSellPrice, int totalTrades, int totaStocks, int
totalVolume) throws SQLException, ClassNotFoundException {
Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement("INSERT
INTO stocks.tb_b3_price(id_stock, price_date, opening_price,
max_price, min_price, avg_price, last_price, best_buy_price,
best_sell_price, total_trades, total_stocks, total_volume)
VALUES((SELECT id FROM stocks.tb_b3_stock WHERE lower(stock_code) =
lower(trim(?))), ?, CAST(? AS money) / 100, CAST(? AS money) / 100,
CAST(? AS money) / 100, CAST(? AS money) / 100, CAST(? AS money) /
100, CAST(? AS money) / 100, CAST(? AS money) / 100, ?, ?, CAST(? AS
double precision) / 100)");
stmt.setString(1, code);
stmt.setDate(2, new java.sql.Date(date.getTime()));
stmt.setInt(3, (int) openingPrice);
stmt.setInt(4, maxPrice);
stmt.setInt(5, minPrice);
stmt.setInt(6, avgPrice);
stmt.setInt(7, lastPrice);
stmt.setInt(8, bestBuyPrice);
stmt.setInt(9, bestSellPrice);
stmt.setInt(10, totalTrades);
stmt.setInt(11, totaStocks);
stmt.setInt(12, totalVolume);
stmt.executeUpdate();
27
conn.close();
}
public static String getSeries(String code) throws SQLException,
ClassNotFoundException {
Connection conn = getConnection();
JSONArray result = new JSONArray();
PreparedStatement stmt = conn.prepareStatement("SELECT
price_date, last_price FROM stocks.tb_b3_price WHERE id_stock =
(SELECT id FROM stocks.tb_b3_stock WHERE lower(stock_code) =
lower(trim(?))) ORDER BY price_date");
stmt.setString(1, code);
ResultSet resSet = stmt.executeQuery();
while(resSet.next()) {
JSONArray value = new JSONArray();
value.put(resSet.getDate("price_date").getTime());
value.put(resSet.getDouble("last_price"));
result.put(value);
}
conn.close();
return result.toString();
}
}
A.3 – Arquivo: B3StockDao.java
/*
* Universidade Federal do Rio de Janeiro
* Escola Politécnica
*
* Este código faz parte do projeto de graduação apresentado por Lucas
de Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
*
* Rio de Janeiro - Set/2018
*/
package br.com.ldev.minervastocks.dao;
import static br.com.ldev.minervastocks.dao.BaseDao.getConnection;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
*
* @author Administrador
*/
public class B3StockDao extends BaseDao {
public static boolean isRegistered(String code) throws
SQLException, ClassNotFoundException {
Connection conn = getConnection();
boolean result;
PreparedStatement stmt = conn.prepareStatement("SELECT 1
FROM stocks.tb_b3_stock WHERE lower(stock_code) = lower(trim(?))");
stmt.setString(1, code);
result = stmt.executeQuery().next();
28
conn.close();
return result;
}
public static void register(String code, String companyIsin, int
market, String spec, int price_factor) throws SQLException,
ClassNotFoundException {
Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement("INSERT
INTO stocks.tb_b3_stock(stock_code, id_company, id_market, id_spec,
price_factor) VALUES(?, (SELECT id FROM stocks.tb_b3_company WHERE
lower(isin_code) = lower(?)), (SELECT id FROM stocks.tb_b3_market
WHERE id_b3 = ?), 0, ?)");
stmt.setString(1, code);
stmt.setString(2, companyIsin);
stmt.setInt(3, market);
//stmt.setString(4, spec); (SELECT id FROM
stocks.tb_b3_spec WHERE lower(id_b3) = lower(?))
stmt.setInt(4, price_factor);
stmt.executeUpdate();
conn.close();
}
}
A.4 – Arquivo: BaseDao.java
/*
* Universidade Federal do Rio de Janeiro
* Escola Politécnica
*
* Este código faz parte do projeto de graduação apresentado por Lucas
de Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
*
* Rio de Janeiro - Set/2018
*/
package br.com.ldev.minervastocks.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
*
* @author Administrador
*/
public class BaseDao {
protected static Connection getConnection() throws SQLException,
ClassNotFoundException {
Class.forName("org.postgresql.Driver");
return
DriverManager.getConnection("jdbc:postgresql://aws.ldev.com.br:5432/st
ocks", "stocks_app", "abc");
}
}
A.5 – Arquivo: OperationsDao.java
29
/*
* Universidade Federal do Rio de Janeiro
* Escola Politécnica
*
* Este código faz parte do projeto de graduação apresentado por Lucas
de Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
*
* Rio de Janeiro - Set/2018
*/
package br.com.ldev.minervastocks.dao;
import static br.com.ldev.minervastocks.dao.BaseDao.getConnection;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import org.json.JSONArray;
/**
*
* @author Administrador
*/
public class OperationsDao extends BaseDao {
public static void add(int receipt, String stock, boolean sell,
int quantity, float price, float cost) throws SQLException,
ClassNotFoundException {
Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement("INSERT
INTO stocks.tb_operation(id_receipt, id_stock, sell_indicator,
quantity, unitary_price, op_cost) VALUES(?, (SELECT id FROM
stocks.tb_b3_stock WHERE lower(stock_code) = lower(trim(?))), ?, ?,
CAST(? AS money) / 100, CAST(? AS money) / 100)");
stmt.setInt(1, receipt);
stmt.setString(2, stock);
stmt.setBoolean(3, sell);
stmt.setInt(4, quantity);
stmt.setInt(5, (int) price * 100);
stmt.setInt(6, (int) cost * 100);
stmt.executeUpdate();
conn.close();
}
public static String list(int user) throws SQLException,
ClassNotFoundException {
Connection conn = getConnection();
JSONArray result = new JSONArray();
PreparedStatement stmt = conn.prepareStatement("SELECT
date, stock_code, sell_indicator, quantity, unitary_price, o.op_cost +
r.op_cost * quantity / sum(quantity) OVER(PARTITION BY r.id) AS
op_cost FROM stocks.tb_receipt r LEFT OUTER JOIN stocks.tb_operation o
ON id_user = ? AND r.id = id_receipt LEFT OUTER JOIN
stocks.tb_b3_stock s ON s.id = o.id_stock ORDER BY date");
stmt.setInt(1, user);
ResultSet resSet = stmt.executeQuery();
30
while(resSet.next()) {
JSONArray value = new JSONArray();
value.put(resSet.getDate("date"));
value.put(resSet.getString("stock_code"));
value.put(resSet.getBoolean("sell_indicator"));
value.put(resSet.getInt("quantity"));
value.put(resSet.getFloat("unitary_price"));
value.put(resSet.getFloat("op_cost"));
result.put(value);
}
conn.close();
return result.toString();
}
}
A.6 – Arquivo: ReceiptDao.java
/*
* Universidade Federal do Rio de Janeiro
* Escola Politécnica
*
* Este código faz parte do projeto de graduação apresentado por Lucas
de Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
*
* Rio de Janeiro - Set/2018
*/
package br.com.ldev.minervastocks.dao;
import static br.com.ldev.minervastocks.dao.BaseDao.getConnection;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
/**
*
* @author Administrador
*/
public class ReceiptDao extends BaseDao {
public static int add(int user, Date date, float cost) throws
SQLException, ClassNotFoundException {
Connection conn = getConnection();
int result;
PreparedStatement stmt = conn.prepareStatement("INSERT
INTO stocks.tb_receipt(id_user, date, op_cost) VALUES(?, ?, CAST(? AS
money) / 100) RETURNING id");
stmt.setInt(1, user);
stmt.setDate(2, new java.sql.Date(date.getTime()));
stmt.setInt(3, (int) cost * 100);
ResultSet resSet = stmt.executeQuery();
resSet.next();
result = resSet.getInt("id");
conn.close();
return result;
31
}
}
A.7 – Arquivo: UserDao.java
/*
* Universidade Federal do Rio de Janeiro
* Escola Politécnica
*
* Este código faz parte do projeto de graduação apresentado por Lucas
de Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
*
* Rio de Janeiro - Set/2018
*/
package br.com.ldev.minervastocks.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
*
* @author Administrador
*/
public class UserDao extends BaseDao {
public static boolean isRegistered(String email) throws
SQLException, ClassNotFoundException {
Connection conn = getConnection();
boolean result;
PreparedStatement stmt = conn.prepareStatement("SELECT 1
FROM stocks.tb_user WHERE lower(email) = lower(?)");
stmt.setString(1, email);
result = stmt.executeQuery().next();
conn.close();
return result;
}
public static boolean hasValidPassword(String email) throws
SQLException, ClassNotFoundException {
Connection conn = getConnection();
boolean result;
PreparedStatement stmt = conn.prepareStatement("SELECT 1
FROM stocks.tb_user WHERE password_hash <> '' AND lower(email) =
lower(?) UNION ALL\n" +
"SELECT 1
FROM stocks.tb_user u INNER JOIN stocks.tb_temp_password p ON u.id =
p.id_user AND expiry_date > current_timestamp AND lower(u.email) =
lower(?)");
stmt.setString(1, email);
stmt.setString(2, email);
result = stmt.executeQuery().next();
conn.close();
return result;
}
32
public static void register(String email) throws SQLException,
ClassNotFoundException {
Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement("INSERT
INTO stocks.tb_user(email) VALUES(?)");
stmt.setString(1, email);
stmt.executeUpdate();
conn.close();
}
public static void setTempPass(String email, String
password_hash) throws SQLException, ClassNotFoundException {
Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement("INSERT
INTO stocks.tb_temp_password(id_user, expiry_date, password_hash)
VALUES((SELECT id FROM stocks.tb_user WHERE lower(email) = lower(?)),
current_timestamp + interval '30 minute', ?)");
stmt.setString(1, email);
stmt.setString(2, password_hash);
stmt.executeUpdate();
conn.close();
}
public static boolean auth(String email, String password_hash)
throws SQLException, ClassNotFoundException {
Connection conn = getConnection();
boolean result;
PreparedStatement stmt = conn.prepareStatement("SELECT 1
FROM stocks.tb_user WHERE lower(email) = lower(?) AND
lower(password_hash) = lower(?)");
stmt.setString(1, email);
stmt.setString(2, password_hash);
result = stmt.executeQuery().next();
conn.close();
return result;
}
public static boolean authTemp(String email, String
password_hash) throws SQLException, ClassNotFoundException {
Connection conn = getConnection();
boolean result;
PreparedStatement stmt = conn.prepareStatement("SELECT 1
FROM stocks.tb_user u INNER JOIN stocks.tb_temp_password p ON
lower(email) = lower(?) AND u.id = p.id_user AND
lower(p.password_hash) = lower(?)");
stmt.setString(1, email);
stmt.setString(2, password_hash);
result = stmt.executeQuery().next();
conn.close();
return result;
33
}
public static void invalidateTempPasswords(String email) throws
SQLException, ClassNotFoundException {
Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement("UPDATE
stocks.tb_temp_password SET expiry_date = CURRENT_TIMESTAMP WHERE
expiry_date > CURRENT_TIMESTAMP AND id_user = (SELECT id FROM
stocks.tb_user WHERE lower(email) = lower(?))");
stmt.setString(1, email);
stmt.executeUpdate();
conn.close();
}
}
A.8 – Arquivo: ImportPricesFromB3.java
/*
* Universidade Federal do Rio de Janeiro
* Escola Politécnica
*
* Este código faz parte do projeto de graduação apresentado por Lucas
de Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
*
* Rio de Janeiro - Set/2018
*/
package br.com.ldev.minervastocks.jobs;
import br.com.ldev.minervastocks.dao.B3CompanyDao;
import br.com.ldev.minervastocks.dao.B3PriceDao;
import br.com.ldev.minervastocks.dao.B3StockDao;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.logging.Logger;
import java.util.zip.ZipInputStream;
/**
*
* @author Administrador
*/
public class ImportPricesFromB3 {
private static final int LINE_SIZE = 245 + 2;
public static void importPrices(Date date) throws
MalformedURLException, IOException, SQLException,
ClassNotFoundException, ParseException {
ZipInputStream zis;
BufferedInputStream win;
byte buffer[] = new byte[LINE_SIZE];
LinkedList<byte []> fullBuffer = new LinkedList();
int bytesRead;
34
win = new BufferedInputStream(new
URL("http://bvmf.bmfbovespa.com.br/InstDados/SerHist/COTAHIST_D" + new
SimpleDateFormat("ddMMyyyy").format(date) + ".ZIP").openStream());
zis = new ZipInputStream(win);
zis.getNextEntry();
while((bytesRead = zis.read(buffer, 0, LINE_SIZE)) != -1)
{
while(bytesRead < LINE_SIZE)
bytesRead += zis.read(buffer, bytesRead,
LINE_SIZE - bytesRead);
fullBuffer.add(buffer);
buffer = new byte[LINE_SIZE];
}
while(!fullBuffer.isEmpty()) {
buffer = fullBuffer.remove();
switch(Integer.parseInt(new String(buffer, 0, 2))) {
case 0:
break;
case 1:
if(Integer.parseInt(new String(buffer,
24, 3)) == 10 || Integer.parseInt(new String(buffer, 24, 3)) == 20) {
if(!B3StockDao.isRegistered(new
String(buffer, 12, 12))) {
if(!B3CompanyDao.isRegistered(new String(buffer, 230, 12))) {
B3CompanyDao.register(new String(buffer, 27, 12), new
String(buffer, 230, 12));
}
B3StockDao.register(new
String(buffer, 12, 12), new String(buffer, 230, 12),
Integer.parseInt(new String(buffer, 24, 3)), new String(buffer, 39,
10), Integer.parseInt(new String(buffer, 210, 7)));
}
B3PriceDao.add(new
String(buffer, 12, 12), new SimpleDateFormat("yyyyMMdd").parse(new
String(buffer, 2, 8)), Integer.parseInt(new String(buffer, 56, 13)),
Integer.parseInt(new String(buffer, 69, 13)), Integer.parseInt(new
String(buffer, 82, 13)), Integer.parseInt(new String(buffer, 95, 13)),
Integer.parseInt(new String(buffer, 108, 13)), Integer.parseInt(new
String(buffer, 121, 13)), Integer.parseInt(new String(buffer, 134,
13)), Integer.parseInt(new String(buffer, 147, 5)),
/*Integer.parseInt(new String(buffer, 152, 18))*/0,
/*Integer.parseInt(new String(buffer, 170, 18))*/0);
}
break;
case 99:
break;
default:
Logger.getAnonymousLogger().severe("Tipo
de registro incorreto");
}
}
}
}
A.9 – Arquivo: StringUtil.java
/*
* Universidade Federal do Rio de Janeiro
35
* Escola Politécnica
*
* Este código faz parte do projeto de graduação apresentado por Lucas
de Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
*
* Rio de Janeiro - Set/2018
*/
package br.com.ldev.minervastocks.util;
/**
*
* @author Administrador
*/
public class StringUtil {
public static final String UPPERCASE =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static final String LOWERCASE =
"abcdefghijklmnopqrstuvwxyz";
public static final String NUMBER = "0123456789";
public static final String ALPHA =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
public static final String ALPHANUM =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
public static final String PASSWORD =
"!#%+23456789:=?@ABCDEFGHJKLMNPRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
public static final String PASSWORDPLUS = "!\"#$%&'()*+,-
./23456789:;<=>?@ABCDEFGHJKLMNOPRSTUVWXYZ[\\]^_abcdefghijkmnopqrstuvwx
yz{|}~";
public static String randomString(int length, String charset) {
if(length == 0)
return "";
return randomString(--length, charset) +
charset.charAt((int) (Math.random() * charset.length()));
}
}
A.10 – Arquivo: AplicationConfig.java
/*
* Universidade Federal do Rio de Janeiro
* Escola Politécnica
*
* Este código faz parte do projeto de graduação apresentado por Lucas
de Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
*
* Rio de Janeiro - Set/2018
*/
package br.com.ldev.minervastocks.ws;
import java.util.Set;
import javax.ws.rs.core.Application;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
/**
*
* @author Administrador
*/
36
@javax.ws.rs.ApplicationPath("rest")
public class ApplicationConfig extends Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<>();
resources.add(MultiPartFeature.class);
addRestResourceClasses(resources);
return resources;
}
/**
* Do not modify addRestResourceClasses() method.
* It is automatically populated with
* all resources defined in the project.
* If required, comment out calling this method in getClasses().
*/
private void addRestResourceClasses(Set<Class<?>> resources) {
resources.add(br.com.ldev.minervastocks.ws.AuthResource.class);
resources.add(br.com.ldev.minervastocks.ws.OperationsResource.cl
ass);
resources.add(br.com.ldev.minervastocks.ws.PriceseriesResource.class);
resources.add(br.com.ldev.minervastocks.ws.UsuarioResource.class
);
}
}
A.11 – Arquivo: AuthResource.java
/*
* Universidade Federal do Rio de Janeiro
* Escola Politécnica
*
* Este código faz parte do projeto de graduação apresentado por Lucas
de Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
*
* Rio de Janeiro - Set/2018
*/
package br.com.ldev.minervastocks.ws;
import br.com.ldev.minervastocks.dao.UserDao;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
37
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import org.glassfish.jersey.media.multipart.FormDataParam;
//import org.glassfish.jersey.media.multipart.FormDataParam;
import org.json.JSONObject;
/**
* REST Web Service
*
* @author Administrador
*/
@Path("auth")
public class AuthResource {
@Context
private UriInfo context;
/**
* Creates a new instance of AuthResource
*/
public AuthResource() {
}
/**
* Retrieves representation of an instance of
br.com.ldev.minervastocks.ws.AuthResource
* @param request
* @param email
* @param password
* @return an instance of java.lang.String
*/
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public String login(@Context HttpServletRequest request,
@FormDataParam("email") String email, @FormDataParam("password")
String password) {
JSONObject response = new JSONObject();
HttpSession session = request.getSession(true);
try {
String password_hash = new BigInteger(1,
MessageDigest.getInstance("SHA-
256").digest(password.getBytes())).toString(16);
if(UserDao.auth(email, password_hash)) {
session.setAttribute("authEmail", email);
UserDao.invalidateTempPasswords(email);
response.put("status", 0);
response.put("statusText", "Ok");
} else if(UserDao.authTemp(email, password_hash)) {
session.setAttribute("changePass", email);
UserDao.invalidateTempPasswords(email);
response.put("status", 1);
} else {
response.put("status", 2);
}
} catch
(SQLException|ClassNotFoundException|NoSuchAlgorithmException ex) {
38
response.put("status", -1);
response.put("statusText", "Error: " +
ex.getMessage());
Logger.getLogger(AuthResource.class.getName()).log(Level.SEVERE,
null, ex);
}
return response.toString();
}
/**
* PUT method for updating or creating an instance of
AuthResource
* @param request
* @param email
* @param password
* @return
*/
@Path("newpass")
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public String changePass(@Context HttpServletRequest request,
@FormDataParam("email") String email, @FormDataParam("password")
String password) {
HttpSession session = request.getSession(true);
if(session.getAttribute("changePass") == null ||
!session.getAttribute("changePass").equals(email)) {
return "";
}
session.removeAttribute("changePass");
session.setAttribute("authEmail", email);
return "";
}
@Path("logout")
@POST
@Produces(MediaType.APPLICATION_JSON)
public String logout(@Context HttpServletRequest request) {
HttpSession session = request.getSession(true);
session.invalidate();
return "";
}
}
A.12 – Arquivo: OperationsResource.java
/*
* Universidade Federal do Rio de Janeiro
* Escola Politécnica
*
* Este código faz parte do projeto de graduação apresentado por Lucas
de Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
*
* Rio de Janeiro - Set/2018
*/
package br.com.ldev.minervastocks.ws;
import br.com.ldev.minervastocks.dao.OperationsDao;
39
import br.com.ldev.minervastocks.dao.ReceiptDao;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PUT;
import javax.ws.rs.core.MediaType;
import org.glassfish.jersey.media.multipart.FormDataParam;
/**
* REST Web Service
*
* @author Administrador
*/
@Path("operations")
public class OperationsResource {
@Context
private UriInfo context;
/**
* Creates a new instance of OperationsResource
*/
public OperationsResource() {
}
/**
* Retrieves representation of an instance of
br.com.ldev.minervastocks.ws.OperationsResource
* @return an instance of java.lang.String
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public String getJson() {
try {
return OperationsDao.list(0);
} catch (SQLException ex) {
Logger.getLogger(OperationsResource.class.getName()).log(Level.S
EVERE, null, ex);
} catch (ClassNotFoundException ex) {
Logger.getLogger(OperationsResource.class.getName()).log(Level.S
EVERE, null, ex);
}
return "";
}
/**
* PUT method for updating or creating an instance of
OperationsResource
* @param date
* @param cost
40
* @param code
* @param quantity
* @param price
* @param opCost
* @return
*/
@PUT
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public String putJson(@FormDataParam("date") String date,
@FormDataParam("cost") float cost, @FormDataParam("code") List<String>
code, @FormDataParam("op") List<String> op, @FormDataParam("quantity")
List<Integer> quantity, @FormDataParam("price") List<Float> price,
@FormDataParam("opCost") List<Float> opCost) {
int receipt = 0;
try {
receipt = ReceiptDao.add(0, new
SimpleDateFormat("yyyy-MM-dd").parse(date), cost);
for(int i = 0; i < code.size(); i++) {
if(!code.get(i).equals(""))
OperationsDao.add(receipt, code.get(i),
op.get(i).equals("sell"), quantity.get(i), price.get(i),
opCost.get(i));
}
} catch (ParseException ex) {
Logger.getLogger(OperationsResource.class.getName()).log(Level.S
EVERE, null, ex);
} catch (SQLException ex) {
Logger.getLogger(OperationsResource.class.getName()).log(Level.S
EVERE, null, ex);
} catch (ClassNotFoundException ex) {
Logger.getLogger(OperationsResource.class.getName()).log(Level.S
EVERE, null, ex);
}
return "Nota: '" + receipt + "'";
}
}
A.13 – Arquivo: PriceseriesResource.java
/*
* Universidade Federal do Rio de Janeiro
* Escola Politécnica
*
* Este código faz parte do projeto de graduação apresentado por Lucas
de Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
*
* Rio de Janeiro - Set/2018
*/
package br.com.ldev.minervastocks.ws;
import br.com.ldev.minervastocks.dao.B3PriceDao;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.core.Context;
41
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PUT;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType;
/**
* REST Web Service
*
* @author Lucas Frucht
*/
@Path("priceseries/{stock}")
public class PriceseriesResource {
@Context
private UriInfo context;
/**
* Creates a new instance of PriceseriesResource
*/
public PriceseriesResource() {
}
/**
* Retrieves representation of an instance of
br.com.ldev.minervastocks.ws.PriceseriesResource
* @param stock
* @return an instance of java.lang.String
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public String getJson(@PathParam("stock") String stock) {
try {
return B3PriceDao.getSeries(stock);
} catch (SQLException ex) {
Logger.getLogger(PriceseriesResource.class.getName()).log(Level.SEVERE
, null, ex);
} catch (ClassNotFoundException ex) {
Logger.getLogger(PriceseriesResource.class.getName()).log(Level.SEVERE
, null, ex);
}
return "";
}
/**
* PUT method for updating or creating an instance of
PriceseriesResource
* @param content representation for the resource
*/
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void putJson(String content) {
}
}
A.14 – Arquivo: UsuarioResource.java
42
/*
* Universidade Federal do Rio de Janeiro
* Escola Politécnica
*
* Este código faz parte do projeto de graduação apresentado por Lucas
de Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
*
* Rio de Janeiro - Set/2018
*/
package br.com.ldev.minervastocks.ws;
import br.com.ldev.minervastocks.dao.UserDao;
import br.com.ldev.minervastocks.util.StringUtil;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PUT;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType;
import org.json.JSONObject;
/**
* REST Web Service
*
* @author Administrador
*/
@Path("usuario/{email}")
public class UsuarioResource {
@Context
private UriInfo context;
/**
* Creates a new instance of UsuarioResource
*/
public UsuarioResource() {
}
/**
* Retrieves representation of an instance of
br.com.ldev.minervastocks.ws.UsuarioResource
* @param email
* @return an instance of java.lang.String
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public String getJson(@PathParam("email") String email) {
JSONObject response = new JSONObject();
String password, password_hash;
try {
43
if(!UserDao.isRegistered(email)) {
response.put("status", 1);
response.put("statusText", "User does not
exist");
response.put("email", email);
return response.toString();
}
if(!UserDao.hasValidPassword(email)) {
password = StringUtil.randomString(16,
StringUtil.PASSWORD);
password_hash = new BigInteger(1,
MessageDigest.getInstance("SHA-
256").digest(password.getBytes())).toString(16);
UserDao.setTempPass(email, password_hash);
response.put("status", 2);
response.put("statusText", "User did not have
a password");
response.put("email", email);
response.put("password", password);
return response.toString();
}
} catch
(SQLException|ClassNotFoundException|NoSuchAlgorithmException ex) {
Logger.getLogger(UsuarioResource.class.getName()).log(Level.SEVE
RE, null, ex);
response.put("status", -1);
response.put("statusText", "Error: " +
ex.getMessage());
return response.toString();
}
response.put("status", 0);
response.put("statusText", "Ok");
response.put("email", email);
return response.toString();
}
/**
* PUT method for updating or creating an instance of
UsuarioResource
* @param email
* @return
*/
@PUT
@Produces(MediaType.APPLICATION_JSON)
public String putJson(@PathParam("email") String email) {
JSONObject response = new JSONObject();
String password, password_hash;
try {
if(UserDao.isRegistered(email)) {
response.put("status", 1);
response.put("statusText", "User already
registered");
response.put("email", email);
return response.toString();
}
44
password = StringUtil.randomString(16,
StringUtil.PASSWORD);
password_hash = new BigInteger(1,
MessageDigest.getInstance("SHA-
256").digest(password.getBytes())).toString(16);
UserDao.register(email);
UserDao.setTempPass(email, password_hash);
} catch
(SQLException|ClassNotFoundException|NoSuchAlgorithmException ex) {
Logger.getLogger(UsuarioResource.class.getName()).log(Level.SEVE
RE, null, ex);
response.put("status", -1);
response.put("statusText", "Error: " +
ex.getMessage());
return response.toString();
}
response.put("status", 0);
response.put("statusText", "Ok");
response.put("email", email);
response.put("password", password);
return response.toString();
}
}
45
Apêndice B
Códigos do Front-end (HTML/JS/CSS)
B.1 – Arquivo: index.html
<!DOCTYPE html>
<!--
Universidade Federal do Rio de Janeiro
Escola Politécnica
Este código faz parte do projeto de graduação apresentado por Lucas de
Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
Rio de Janeiro - Set/2018
-->
<html>
<head>
<title>MinervaStocks</title>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8">
<script>
function emailSubmit(ev) {
let ajax = new XMLHttpRequest();
ajax.open("GET", "rest/usuario/" +
document.getElementById("email").value, true);
ajax.onreadystatechange = emailValidate;
ajax.send();
ev.preventDefault();
return false;
}
function emailValidate() {
if (this.readyState === 4 && this.status === 200) {
response = JSON.parse(this.responseText);
document.body.removeChild(document.getElementById("emailForm"));
if(response.status === 0) {
let form = document.createElement("form");
form.id = "passwordForm";
let label = document.createElement("label");
let input = document.createElement("input");
label.textContent = "Senha: ";
label.for = "password";
input.type = "password";
input.id = "password";
input.name = "password";
form.appendChild(label);
form.appendChild(input);
input = document.createElement("input");
input.type = "hidden";
46
input.value = response.email;
input.name = "email";
input.id = "email";
form.appendChild(input);
input = document.createElement("input");
input.type = "submit";
form.appendChild(input);
input = document.createElement("input");
input.type = "reset";
form.appendChild(input);
form.onsubmit = passwordSubmit;
document.body.appendChild(form);
} else if(response.status === 2) {
let p = document.createElement("p");
p.textContent = "Senha provisória expirada. Uma nova
senha foi enviada para seu e-mail.";
document.body.appendChild(p);
if(true) {
p = document.createElement("p");
p.textContent = "Versão de testes!! Nenhum e-
mail foi enviado! Senha: " + response.password;
document.body.appendChild(p);
}
let form = document.createElement("form");
form.id = "passwordForm";
let label = document.createElement("label");
let input = document.createElement("input");
label.textContent = "Senha: ";
label.for = "password";
input.type = "password";
input.id = "password";
input.name = "password";
form.appendChild(label);
form.appendChild(input);
input = document.createElement("input");
input.type = "submit";
form.appendChild(input);
input = document.createElement("input");
input.type = "reset";
form.appendChild(input);
form.onsubmit = passwordSubmit;
document.body.appendChild(form);
} else /*if(response.status === 1)*/ {
let form = document.createElement("form");
form.id = "signupForm";
let p = document.createElement("p");
p.textContent = "E-mail não cadastrado. Deseja se
cadastrar?";
form.appendChild(p);
p = document.createElement("p");
let input = document.createElement("input");
input.type = "hidden";
input.value = response.email;
input.name = "email";
input.id = "email";
p.appendChild(input);
input = document.createElement("input");
input.type = "submit";
p.appendChild(input);
input = document.createElement("input");
input.type = "reset";
47
p.appendChild(input);
form.appendChild(p);
form.onsubmit = signupSubmit;
document.body.appendChild(form);
}
}
}
function passwordSubmit(ev) {
let ajax = new XMLHttpRequest();
ajax.open("POST", "rest/auth", true);
ajax.onreadystatechange = passwordValidate;
ajax.send(new
FormData(document.getElementById("passwordForm")));
ev.preventDefault();
return false;
}
function passwordValidate() {
if (this.readyState === 4 && this.status === 200) {
response = JSON.parse(this.responseText);
document.body.removeChild(document.getElementById("passwordForm"
));
if(response.status === 0) {
location.href = "dashbord.html";
} else if(response.status === 1) {
let form = document.createElement("form");
form.id = "passwordForm";
let p = document.createElement("p");
p.textContent = "Cadastre a sua nova senha.";
form.appendChild(p);
p = document.createElement("p");
let label = document.createElement("label");
let input = document.createElement("input");
label.textContent = "Senha: ";
label.for = "password";
input.type = "password";
input.id = "password";
input.name = "password";
p.appendChild(label);
p.appendChild(input);
input = document.createElement("input");
input.type = "submit";
p.appendChild(input);
input = document.createElement("input");
input.type = "reset";
p.appendChild(input);
form.onsubmit = passwordSubmit;
form.appendChild(p);
document.body.appendChild(form);
} else /*if(response.status === 2)*/{
let form = document.createElement("form");
form.id = "forgotForm";
let p = document.createElement("p");
p.textContent = "Senha inválida. Deseja que uma nova
senha seja gerada e eviada para seu e-mail?";
form.appendChild(p);
p = document.createElement("p");
let input = document.createElement("input");
input.type = "hidden";
48
input.value = response.email;
input.name = "email";
input.id = "email";
p.appendChild(input);
input = document.createElement("input");
input.type = "submit";
p.appendChild(input);
input = document.createElement("input");
input.type = "reset";
p.appendChild(input);
form.appendChild(p);
form.onsubmit = forgotSubmit;
document.body.appendChild(form);
}
}
}
function signupSubmit(ev) {
let ajax = new XMLHttpRequest();
ajax.open("PUT", "rest/usuario/" +
document.getElementById("email").value, true);
ajax.onreadystatechange = signupValidate;
ajax.send();
ev.preventDefault();
return false;
}
function signupValidate() {
if (this.readyState === 4 && this.status === 200) {
response = JSON.parse(this.responseText);
document.body.removeChild(document.getElementById("signupForm"))
;
if(response.status === 0) {
let form = document.createElement("form");
form.id = "passwordForm";
let p = document.createElement("p");
p.textContent = "Usuário cadastrado com sucesso. Uma
senha provisória foi enviada para seu e-mail.";
form.appendChild(p);
if(true) {
p = document.createElement("p");
p.textContent = "Versão de testes!! Nenhum e-
mail foi enviado! Senha: " + response.password;
form.appendChild(p);
}
p = document.createElement("p");
let label = document.createElement("label");
let input = document.createElement("input");
input.type = "hidden";
input.value = response.email;
input.name = "email";
input.id = "email";
p.appendChild(input);
input = document.createElement("input");
label.textContent = "Senha: ";
label.for = "password";
input.type = "password";
input.id = "password";
input.name = "password";
p.appendChild(label);
49
p.appendChild(input);
input = document.createElement("input");
input.type = "submit";
p.appendChild(input);
input = document.createElement("input");
input.type = "reset";
p.appendChild(input);
form.appendChild(p);
form.onsubmit = passwordSubmit;
document.body.appendChild(form);
} else {
let form = document.createElement("form");
let p = document.createElement("p");
p.textContent = "E-mail não cadastrado. Deseja se
cadastrar?";
form.appendChild(p);
p = document.createElement("p");
let input = document.createElement("input");
input.type = "submit";
p.appendChild(input);
input = document.createElement("input");
input.type = "reset";
p.appendChild(input);
form.appendChild(p);
form.onsubmit = signupSubmit;
document.body.appendChild(form);
}
}
}
window.onload = () => {
document.getElementById("emailForm").onsubmit = emailSubmit;
};
</script>
</head>
<body>
<h1>Minerva Stocks</h1>
<form id="emailForm">
<label for="email">E-mail: </label><input
type="email" id="email" name="email"/><input type="submit"/><input
type="reset"/>
</form>
</body>
</html>
B.1 – Arquivo: main.html
<!DOCTYPE html>
<!--
Universidade Federal do Rio de Janeiro
Escola Politécnica
Este código faz parte do projeto de graduação apresentado por Lucas de
Carvalho Frucht como parte dos requisitos necessários à obtenção do
título de Engenheiro Eletrônico e de Computação.
Rio de Janeiro - Set/2018
-->
<html>
<head>
<title>TODO supply a title</title>
50
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,
initial-scale=1.0">
<script
src="https://code.highcharts.com/stock/highstock.js"></script>
<script>
function clearMainArea() {
while(document.getElementById("main").hasChildNodes()) {
document.getElementById("main").removeChild(document.getElementB
yId("main").firstChild);
}
}
function addReceiptLine() {
let p, label, input, opt;
p = document.createElement("p");
label = document.createElement("label");
label.textContent = "Código:\xa0";
label.htmlFor = "code" +
(document.getElementById("receiptForm").childNodes.length - 2);
p.appendChild(label);
input = document.createElement("input");
input.name = "code";
input.id = "code" +
(document.getElementById("receiptForm").childNodes.length - 2);
input.onchange = (ev) => {
if(ev.target.value) {
if(ev.target.nextElementSibling.nextElementSibling.disabled) {
ev.target.nextElementSibling.nextElementSibling.disabled =
false;
ev.target.nextElementSibling.nextElementSibling.focus();
ev.target.nextElementSibling.nextElementSibling.nextElementSibli
ng.nextElementSibling.disabled = false;
ev.target.nextElementSibling.nextElementSibling.nextElementSibli
ng.nextElementSibling.nextElementSibling.nextElementSibling.disabled =
false;
ev.target.nextElementSibling.nextElementSibling.nextElementSibli
ng.nextElementSibling.nextElementSibling.nextElementSibling.nextElemen
tSibling.nextElementSibling.disabled = false;
addReceiptLine();
}
} else {
ev.target.parentElement.parentElement.removeChild(ev.target.pare
ntElement);
}
};
p.appendChild(input);
label = document.createElement("label");
label.textContent = "Operação: \xa0";
label.htmlFor = "op" +
(document.getElementById("receiptForm").childNodes.length - 2);
p.appendChild(label);
input = document.createElement("select");
51
opt = document.createElement("option");
opt.value = "buy";
opt.textContent = "Compra";
input.appendChild(opt);
opt = document.createElement("option");
opt.value = "sell";
opt.textContent = "Venda";
input.appendChild(opt);
input.disabled = true;
input.name = "op";
input.id = "op" +
(document.getElementById("receiptForm").childNodes.length - 2);
p.appendChild(input);
label = document.createElement("label");
label.textContent = "Quantidade:\xa0";
label.htmlFor = "quantity" +
(document.getElementById("receiptForm").childNodes.length - 2);
p.appendChild(label);
input = document.createElement("input");
input.type = "number";
input.value = "0";
input.disabled = true;
input.name = "quantity";
input.id = "quantity" +
(document.getElementById("receiptForm").childNodes.length - 2);
p.appendChild(input);
label = document.createElement("label");
label.textContent = "Preço:\xa0";
label.htmlFor = "price" +
(document.getElementById("receiptForm").childNodes.length - 2);
p.appendChild(label);
input = document.createElement("input");
input.type = "number";
input.value = "0";
input.step = "0.01";
input.disabled = true;
input.name = "price";
input.id = "price" +
(document.getElementById("receiptForm").childNodes.length - 2);
p.appendChild(input);
label = document.createElement("label");
label.textContent = "Custo:\xa0";
label.htmlFor = "opCost" +
(document.getElementById("receiptForm").childNodes.length - 2);
p.appendChild(label);
input = document.createElement("input");
input.type = "number";
input.value = "0";
input.step = "0.01";
input.disabled = true;
input.name = "opCost";
input.id = "opCost" +
(document.getElementById("receiptForm").childNodes.length - 2);
p.appendChild(input);
document.getElementById("receiptForm").insertBefore(p,
document.getElementById("receiptForm").lastElementChild);
}
function startReceiptForm(ev) {
let form, p, label, input;
clearMainArea();
52
form = document.createElement("form");
form.id = "receiptForm";
form.onsubmit = (ev) => {
let ajax = new XMLHttpRequest();
ajax.open("PUT", "rest/operations", true);
ajax.send(new FormData(receiptForm));
ev.preventDefault();
return false;
};
form.onreset = (ev) => {
document.getElementById("date").value = "";
ev.preventDefault();
return false;
};
p = document.createElement("p");
label = document.createElement("label");
label.textContent = "Data:\xa0";
label.htmlFor = "date";
p.appendChild(label);
input = document.createElement("input");
input.type = "date";
input.id = "date";
input.name = "date";
input.onchange = (ev) => {
if(ev.target.value &&
ev.target.nextElementSibling.nextElementSibling.disabled) {
ev.target.nextElementSibling.nextElementSibling.disabled =
false;
ev.target.nextElementSibling.nextElementSibling.focus();
addReceiptLine();
}
if(!ev.target.value &&
!ev.target.nextElementSibling.nextElementSibling.disabled) {
ev.target.nextElementSibling.nextElementSibling.disabled = true;
while(ev.target.parentElement.nextElementSibling !==
ev.target.parentElement.parentElement.lastChild)
ev.target.parentElement.parentElement.removeChild(ev.target.pare
ntElement.nextElementSibling);
}
};
p.appendChild(input);
label = document.createElement("label");
label.textContent = "Custos:\xa0";
label.htmlFor = "cost";
p.appendChild(label);
input = document.createElement("input");
input.type = "number";
input.value = "0";
input.step = "0.01";
input.disabled = true;
input.id = "cost";
input.name = "cost";
p.appendChild(input);
form.appendChild(p);
p = document.createElement("p");
input = document.createElement("input");
input.type = "submit";
53
p.appendChild(input);
input = document.createElement("input");
input.type = "reset";
p.appendChild(input);
form.appendChild(p);
document.getElementById("main").appendChild(form);
ev.preventDefault();
return false;
}
function listOperations(ev) {
let ajax = new XMLHttpRequest();
clearMainArea();
ajax.open("GET", "rest/operations", true);
ajax.onreadystatechange = function() {
if(this.readyState === 4 && this.status === 200) {
let table, tr, td;
table = document.createElement("table");
tr = document.createElement("tr");
td = document.createElement("th");
td.textContent = "Data";
tr.appendChild(td);
td = document.createElement("th");
td.textContent = "Ação";
tr.appendChild(td);
td = document.createElement("th");
td.textContent = "Operação";
tr.appendChild(td);
td = document.createElement("th");
td.textContent = "Quantidade";
tr.appendChild(td);
td = document.createElement("th");
td.textContent = "Preço unitário";
tr.appendChild(td);
td = document.createElement("th");
td.textContent = "Custos";
tr.appendChild(td);
table.appendChild(tr);
JSON.parse(this.responseText).forEach((line) => {
tr = document.createElement("tr");
td = document.createElement("td");
td.textContent = line[0];
tr.appendChild(td);
td = document.createElement("td");
td.textContent = line[1];
tr.appendChild(td);
td = document.createElement("td");
td.textContent = line[2] ? "Venda" : "Compra";
tr.appendChild(td);
td = document.createElement("td");
td.textContent = line[3];
tr.appendChild(td);
td = document.createElement("td");
td.textContent = line[4];
tr.appendChild(td);
td = document.createElement("td");
td.textContent = line[5];
tr.appendChild(td);
table.appendChild(tr);
});
document.getElementById("main").appendChild(table);
54
}
};
ajax.send();
ev.preventDefault();
return false;
}
function startChart(ev) {
let form, input, div;
clearMainArea();
form = document.createElement("form");
input = document.createElement("input");
input.name = "code";
input.id = "code";
form.appendChild(input);
input = document.createElement("input");
input.type = "submit";
form.appendChild(input);
form.onsubmit = (ev) => {
let ajax = new XMLHttpRequest();
ajax.open("GET", "rest/priceseries/" +
document.getElementById("code").value, true);
ajax.onreadystatechange = function() {
if(this.readyState === 4 && this.status === 200) {
var chart = Highcharts.stockChart('chart', {
rangeSelector: {
selected: 1
},
title: {
text: 'Teste'
},
series: [{
name: "PETR3",
data:
JSON.parse(this.responseText),
tooltip: {
valueDecimals: 2
}
}]
});
}
};
ajax.send();
ev.preventDefault();
return false;
};
document.getElementById("main").appendChild(form);
div = document.createElement("div");
div.id = "chart";
document.getElementById("main").appendChild(div);
ev.preventDefault();
return false;
}
window.onload = (ev) => {
let a, ul;
a = document.createElement("a");
a.href="#";
a.onclick = startChart;
a.textContent = "Série Histórica";
ul = document.createElement("ul");
55
ul.appendChild(a);
document.getElementById("menu").appendChild(ul);
a = document.createElement("a");
a.href="#";
a.onclick = startReceiptForm;
a.textContent = "Cadastrar nota";
ul = document.createElement("ul");
ul.appendChild(a);
document.getElementById("menu").appendChild(ul);
a = document.createElement("a");
a.href="#";
a.onclick = listOperations;
a.textContent = "Listar Operações";
ul = document.createElement("ul");
ul.appendChild(a);
document.getElementById("menu").appendChild(ul);
};
</script>
</head>
<body>
<h1>Minerva Stocks</h1>
<nav><ul id="menu"></ul></nav>
<main id="main">
</main>
</body>
</html>
56
Apêndice C
Scripts de Banco de Dados
CREATE SCHEMA stocks;
GRANT USAGE ON SCHEMA stocks TO stocks_app;
-- Table: stocks.tb_b3_spec
CREATE TABLE stocks.tb_b3_spec
(
id smallserial NOT NULL PRIMARY KEY,
id_b3 character(10) NOT NULL UNIQUE,
name character(64) NOT NULL
);
GRANT SELECT ON TABLE stocks.tb_b3_spec TO stocks_app;
-- Table: stocks.tb_b3_market
-- DROP TABLE stocks.tb_b3_market;
CREATE TABLE stocks.tb_b3_market
(
id smallserial NOT NULL PRIMARY KEY,
id_b3 smallint NOT NULL UNIQUE,
name character(64) NOT NULL
);
GRANT SELECT ON TABLE stocks.tb_b3_market TO stocks_app;
-- Table: stocks.tb_b3_company
CREATE TABLE stocks.tb_b3_company
(
id serial NOT NULL PRIMARY KEY,
name character(12) NOT NULL UNIQUE,
isin_code character(12) NOT NULL UNIQUE
);
GRANT INSERT, SELECT ON TABLE stocks.tb_b3_company TO stocks_app;
GRANT USAGE ON SEQUENCE stocks.tb_b3_company_id_seq TO stocks_app;
-- Table: stocks.tb_b3_stock
CREATE TABLE stocks.tb_b3_stock
(
id serial NOT NULL PRIMARY KEY,
stock_code character(12) NOT NULL UNIQUE,
id_company integer NOT NULL REFERENCES stocks.tb_b3_company(id),
id_market smallint NOT NULL REFERENCES stocks.tb_b3_market(id),
57
id_spec smallint NOT NULL REFERENCES stocks.tb_b3_spec(id),
price_factor integer NOT NULL
);
GRANT INSERT, SELECT ON TABLE stocks.tb_b3_stock TO stocks_app;
GRANT USAGE ON SEQUENCE stocks.tb_b3_stock_id_seq TO stocks_app;
-- Table: stocks.tb_b3_price
CREATE TABLE stocks.tb_b3_price
(
id_stock integer NOT NULL REFERENCES stocks.tb_b3_stock(id),
price_date date NOT NULL,
opening_price money NOT NULL,
max_price money NOT NULL,
min_price money NOT NULL,
avg_price money NOT NULL,
last_price money NOT NULL,
best_buy_price money NOT NULL,
best_sell_price money NOT NULL,
total_trades integer NOT NULL,
total_stocks integer NOT NULL,
total_volume double precision NOT NULL,
CONSTRAINT tb_b3_prices_pkey PRIMARY KEY (id_stock, price_date)
);
GRANT INSERT, SELECT ON TABLE stocks.tb_b3_price TO stocks_app;
-- Table: stocks.tb_user
CREATE TABLE stocks.tb_user
(
id serial NOT NULL PRIMARY KEY,
email character(128) NOT NULL UNIQUE,
password_hash character(64) NOT NULL DEFAULT '',
creation_date timestamp with time zone NOT NULL DEFAULT
current_timestamp
);
GRANT SELECT ON TABLE stocks.tb_user TO stocks_app;
GRANT INSERT(email) ON stocks.tb_user TO stocks_app;
GRANT UPDATE(password_hash) ON stocks.tb_user TO stocks_app;
GRANT USAGE ON SEQUENCE stocks.tb_user_id_seq TO stocks_app;
-- Table: stocks.tb_temp_password
CREATE TABLE stocks.tb_temp_password
(
id_user integer NOT NULL REFERENCES stocks.tb_user(id),
creation_date timestamp with time zone NOT NULL DEFAULT
current_timestamp,
expiry_date timestamp with time zone NOT NULL,
password_hash character(64) NOT NULL,
CONSTRAINT tb_temp_password_pkey PRIMARY KEY (id_user,
creation_date)
);
GRANT SELECT ON TABLE stocks.tb_temp_password TO stocks_app;
GRANT INSERT(id_user) ON stocks.tb_temp_password TO stocks_app;
GRANT INSERT(expiry_date), UPDATE(expiry_date) ON
stocks.tb_temp_password TO stocks_app;
58
GRANT INSERT(password_hash) ON stocks.tb_temp_password TO stocks_app;
-- Table: stocks.tb_receipt
CREATE TABLE stocks.tb_receipt
(
id serial NOT NULL PRIMARY KEY,
id_user integer NOT NULL REFERENCES stocks.tb_user(id),
date date NOT NULL,
op_cost money NOT NULL
);
GRANT SELECT ON TABLE stocks.tb_receipt TO stocks_app;
GRANT INSERT(id_user) ON stocks.tb_receipt TO stocks_app;
GRANT INSERT(date) ON stocks.tb_receipt TO stocks_app;
GRANT INSERT(op_cost) ON stocks.tb_receipt TO stocks_app;
GRANT USAGE ON SEQUENCE stocks.tb_receipt_id_seq TO stocks_app;
-- Table: stocks.tb_operation
-- DROP TABLE stocks.tb_operation;
CREATE TABLE stocks.tb_operation
(
id bigserial NOT NULL PRIMARY KEY,
id_receipt integer NOT NULL REFERENCES stocks.tb_receipt(id),
id_stock integer NOT NULL REFERENCES stocks.tb_b3_stock(id),
sell_indicator boolean NOT NULL,
quantity integer NOT NULL,
unitary_price money NOT NULL,
op_cost money NOT NULL
);
GRANT SELECT ON TABLE stocks.tb_operation TO stocks_app;
GRANT INSERT(id_receipt) ON stocks.tb_operation TO stocks_app;
GRANT INSERT(id_stock) ON stocks.tb_operation TO stocks_app;
GRANT INSERT(sell_indicator) ON stocks.tb_operation TO stocks_app;
GRANT INSERT(quantity) ON stocks.tb_operation TO stocks_app;
GRANT INSERT(unitary_price) ON stocks.tb_operation TO stocks_app;
GRANT INSERT(op_cost) ON stocks.tb_operation TO stocks_app;
GRANT USAGE ON SEQUENCE stocks.tb_operation_id_seq TO stocks_app;
59
Anexo D
Licença da biblioteca HighCharts
Lucas, here is your Licence Statement
Highsoft <[email protected]> 2 de setembro de 2018 19:40 Para: Lucas De Carvalho Frucht <[email protected]>
Thank you for your inquiry. License statement enclosed.
1.Non-Commercial Licence Statement
Thank you for your interest in our software. This email constitutes your licence statement.
License holder: Lucas De Carvalho Frucht Universidade Federal do Rio de Janeiro
This license is valid for Student for the following product(s): Highcharts,Highstock,Highmaps
This software is released under Creative Commons Attribution-NonCommercial 3.0, and is available for download at highcharts.com/download. No further activation or license key is required.
Should you require a commercial licence in the future, please visit our shop.
60
Get the most value from our products by subscribing to one of our mailing lists. Click the button below to select.
Get product news and updates
Connect With Us
www.highcharts.com
Sentrumsgata 44 • Vik, i Sogn 6893 • Norway • Click here to unsubscribe.