SISTEMA PARA AUXÍLIO E GESTÃO DE INVESTIMENTOS...

74
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

Transcript of SISTEMA PARA AUXÍLIO E GESTÃO DE INVESTIMENTOS...

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:&nbsp;</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.