Commit 2edafb02 authored by Emmanuel Raviart's avatar Emmanuel Raviart
Browse files

Rename stock to security.

parent 1d42f894
......@@ -15,7 +15,7 @@ Edit `src/server-config.js` to change database informations. Then
npm install
```
## Retrieve or generate JSON files of issuers, stocks, etc
## Retrieve or generate JSON files of issuers, securities, etc
### Either retrieve JSON files from their git repository
......@@ -28,7 +28,7 @@ git clone https://gitlab.huma-num.fr/eurhisfirm/dfih-open-data.git data
```bash
node_modules/.bin/babel-node scripts/generate-issuers.js
node_modules/.bin/babel-node scripts/generate-stock-exchanges.js
node_modules/.bin/babel-node scripts/generate-stocks.js
node_modules/.bin/babel-node scripts/generate-securities.js
```
## Launch server
......
......@@ -14,7 +14,7 @@
<ul>
{#each rows as row (row[0])}
<li>
<a id="{row[2]}" href="/stocks/{row[0]}/prices" data-container="body" data-trigger="manual" data-toggle="popover"
<a id="{row[2]}" href="/securities/{row[0]}/prices" data-container="body" data-trigger="manual" data-toggle="popover"
data-html="true" data-placement="top" data-content="Loading price data..." on:mouseenter="popoverHover(event)"
on:mouseleave="popoverLeave(event)">{row[1]}</a>
</li>
......
......@@ -3,123 +3,123 @@ import oracledb from "oracledb"
import { pool } from "../../../../database"
export async function get(req, res) {
const { stock: stockId } = req.params
const { security: securityId } = req.params
const connection = await pool.getConnection()
let result = null
try {
result = await connection.execute(
`
select comptant.notation,
select comptant.notation,
(comptant.day - to_date('1970/01/01','YYYY/MM/DD'))*86400000 as EPOCH,
comptant.prix as prix_comptant,
terme.Open_Liq15, terme.Open_LiqFin,
terme.Close_Liq15, terme.Close_LiqFin,
terme.Min_Liq15, terme.Min_LiqFin,
terme.Max_Liq15, terme.Max_LiqFin
comptant.prix as prix_comptant,
terme.Open_Liq15, terme.Open_LiqFin,
terme.Close_Liq15, terme.Close_LiqFin,
terme.Min_Liq15, terme.Min_LiqFin,
terme.Max_Liq15, terme.Max_LiqFin
from
(
select last_value(notation ignore nulls) over (order by day, notation) as notation,
day,
max(Open_Liq15) as Open_Liq15, max(Open_LiqFin) as Open_LiqFin,
day,
max(Open_Liq15) as Open_Liq15, max(Open_LiqFin) as Open_LiqFin,
max(Close_Liq15) as Close_Liq15, max(Close_LiqFin) as Close_LiqFin,
min(Min_Liq15) as Min_Liq15, min(Min_LiqFin) as Min_LiqFin,
max(Max_Liq15) as Max_Liq15, max(Max_LiqFin) as Max_LiqFin
FROM
(select c.notation, cd.exact_date as day,
nvl(c.Open_Liq15, c.Open_Liq151) as Open_Liq15,
(select c.notation, cd.exact_date as day,
nvl(c.Open_Liq15, c.Open_Liq151) as Open_Liq15,
nvl(c.Open_LiqFin, c.Open_LiqFin1) as Open_LiqFin,
nvl(c.Close_Liq15, c.Close_Liq151) as Close_Liq15,
nvl(c.Close_Liq15, c.Close_Liq151) as Close_Liq15,
nvl(c.Close_LiqFin, c.Close_LiqFin1) as Close_LiqFin,
nvl(c.Min_Liq15, c.Min_Liq151) as Min_Liq15,
nvl(c.Min_Liq15, c.Min_Liq151) as Min_Liq15,
nvl(c.Min_LiqFin, c.Min_LiqFin1) as Min_LiqFin,
nvl(c.Max_Liq15, c.Max_Liq151) as Max_Liq15,
nvl(c.Max_Liq15, c.Max_Liq151) as Max_Liq15,
nvl(c.Max_LiqFin, c.Max_LiqFin1) as Max_LiqFin
from
from
control_dates cd
left join
(
select np.day, np.notation,
decode(np.setdate, 20, np.open) Open_Liq15,
decode(np.setdate, 15, np.open, 17, np.open, 18, np.open, 19, np.open) Open_LiqFin,
(case when extract(day from np.day) between 10 and 20
select np.day, np.notation,
decode(np.setdate, 20, np.open) Open_Liq15,
decode(np.setdate, 15, np.open, 17, np.open, 18, np.open, 19, np.open) Open_LiqFin,
(case when extract(day from np.day) between 10 and 20
then decode(np.setdate, 13, np.open) END) as Open_Liq151,
(case when extract(day from np.day) < 10 or extract(day from np.day) > 20
(case when extract(day from np.day) < 10 or extract(day from np.day) > 20
then decode(np.setdate, 13, np.open) END) as Open_LiqFin1,
decode(np.setdate, 20, np.close) Close_Liq15,
decode(np.setdate, 15, np.close, 17, np.close, 18, np.close, 19, np.close) Close_LiqFin,
(case when extract(day from np.day) between 10 and 20
decode(np.setdate, 20, np.close) Close_Liq15,
decode(np.setdate, 15, np.close, 17, np.close, 18, np.close, 19, np.close) Close_LiqFin,
(case when extract(day from np.day) between 10 and 20
then decode(np.setdate, 13, np.close) END) as Close_Liq151,
(case when extract(day from np.day) < 10 or extract(day from np.day) > 20
(case when extract(day from np.day) < 10 or extract(day from np.day) > 20
then decode(np.setdate, 13, np.close) END) as Close_LiqFin1,
decode(np.setdate, 20, np.min) Min_Liq15,
decode(np.setdate, 15, np.min, 17, np.min, 18, np.min, 19, np.min) Min_LiqFin,
(case when extract(day from np.day) between 10 and 20
decode(np.setdate, 20, np.min) Min_Liq15,
decode(np.setdate, 15, np.min, 17, np.min, 18, np.min, 19, np.min) Min_LiqFin,
(case when extract(day from np.day) between 10 and 20
then decode(np.setdate, 13, np.min) END) as Min_Liq151,
(case when extract(day from np.day) < 10 or extract(day from np.day) > 20
(case when extract(day from np.day) < 10 or extract(day from np.day) > 20
then decode(np.setdate, 13, np.min) END) as Min_LiqFin1,
decode(np.setdate, 20, np.max) Max_Liq15,
decode(np.setdate, 15, np.max, 17, np.max, 18, np.max, 19, np.max) Max_LiqFin,
(case when extract(day from np.day) between 10 and 20
decode(np.setdate, 20, np.max) Max_Liq15,
decode(np.setdate, 15, np.max, 17, np.max, 18, np.max, 19, np.max) Max_LiqFin,
(case when extract(day from np.day) between 10 and 20
then decode(np.setdate, 13, np.max) END) as Max_Liq151,
(case when extract(day from np.day) < 10 or extract(day from np.day) > 20
(case when extract(day from np.day) < 10 or extract(day from np.day) > 20
then decode(np.setdate, 13, np.max) END) as Max_LiqFin1
from notation_price np
where np.notation in (select id from notation where stock = :stockId)
from notation_price np
where np.notation in (select id from notation where stock = :securityId)
and np.market = 1
) c ON (cd.exact_date = c.day)
where nvl(cd.exact_date, c.day) >= (select min(startdate) from notation where stock = :stockId)
and nvl(cd.exact_date, c.day) <= (select max(enddate) from notation where stock = :stockId)
where nvl(cd.exact_date, c.day) >= (select min(startdate) from notation where stock = :securityId)
and nvl(cd.exact_date, c.day) <= (select max(enddate) from notation where stock = :securityId)
)
group by notation, day
) terme
join
(
select distinct last_value(notation ignore nulls) over (order by day, notation) as notation,
day,
select distinct last_value(notation ignore nulls) over (order by day, notation) as notation,
day,
last_value(prix ignore nulls) over (order by day, notation) as prix
from
(
select nvl(pnep.notation, pnp.notation) as notation,
nvl(pnep.day, pnp.day) as day,
nvl(pnep.prix, pnp.prix) as prix
from
select nvl(pnep.notation, pnp.notation) as notation,
nvl(pnep.day, pnp.day) as day,
nvl(pnep.prix, pnp.prix) as prix
from
(
select notation, day, dernier_prix_comptant as prix from
select notation, day, dernier_prix_comptant as prix from
(
select distinct a.notation, cd.exact_date as day, a.dernier_prix_comptant
from
from
control_dates cd
left join
(
select nep.day, nep.notation,
first_value(nep.price)
select nep.day, nep.notation,
first_value(nep.price)
over (partition by nep.day, nep.notation order by nep.id desc) as dernier_prix_comptant
from notation_extra_price nep where nep.notation in (select id from notation where stock = :stockId)
from notation_extra_price nep where nep.notation in (select id from notation where stock = :securityId)
) a ON (cd.exact_date = a.day)
where nvl(cd.exact_date, a.day) >= (select min(startdate) from notation where stock = :stockId)
and nvl(cd.exact_date, a.day) <= (select max(enddate) from notation where stock = :stockId)
where nvl(cd.exact_date, a.day) >= (select min(startdate) from notation where stock = :securityId)
and nvl(cd.exact_date, a.day) <= (select max(enddate) from notation where stock = :securityId)
)
) pnep
full outer join
(
select notation, day, dernier_prix_comptant as prix from
select notation, day, dernier_prix_comptant as prix from
(
select distinct b.notation, nvl(cd.exact_date, b.day) as day, b.dernier_prix_comptant
from
from
control_dates cd
left join
(
select np.day, np.notation, nvl(np.price_yesterday, np.previous) as dernier_prix_comptant
from notation_price np
where np.notation in (select id from notation where stock = :stockId)
from notation_price np
where np.notation in (select id from notation where stock = :securityId)
and np.market = 2
) b ON (cd.exact_date = b.day)
where nvl(cd.exact_date, b.day) >= (select min(startdate) from notation where stock = :stockId)
and nvl(cd.exact_date, b.day) <= (select max(enddate) from notation where stock = :stockId)
where nvl(cd.exact_date, b.day) >= (select min(startdate) from notation where stock = :securityId)
and nvl(cd.exact_date, b.day) <= (select max(enddate) from notation where stock = :securityId)
)
) pnp
ON (pnep.notation = pnp.notation and pnep.day = pnp.day)
......@@ -128,7 +128,7 @@ export async function get(req, res) {
order by comptant.day
`,
{
stockId: { dir: oracledb.BIND_IN, val: stockId, type: oracledb.STRING },
securityId: { dir: oracledb.BIND_IN, val: securityId, type: oracledb.STRING },
}
)
} catch (error) {
......
......@@ -4,9 +4,9 @@ import path from "path"
import serverConfig from "../../../../server-config"
export function get(req, res) {
const { stock: stockId } = req.params
const stockFilePath = path.join(serverConfig.dataDir, "stocks", `stock_${stockId}.json`)
if (!fs.existsSync(stockFilePath)) {
const { security: securityId } = req.params
const securityFilePath = path.join(serverConfig.dataDir, "securities", `security_${securityId}.json`)
if (!fs.existsSync(securityFilePath)) {
res.writeHead(404, {
"Content-Type": "application/json",
})
......@@ -15,7 +15,7 @@ export function get(req, res) {
{
error: {
code: 404,
message: `Stock ${stockId} doesn't exist.`,
message: `Security ${securityId} doesn't exist.`,
},
},
null,
......@@ -23,7 +23,7 @@ export function get(req, res) {
)
)
}
if (!fs.lstatSync(stockFilePath).isFile()) {
if (!fs.lstatSync(securityFilePath).isFile()) {
res.writeHead(404, {
"Content-Type": "application/json",
})
......@@ -32,7 +32,7 @@ export function get(req, res) {
{
error: {
code: 404,
message: `Stock ${stockId} doesn't exist (not a file).`,
message: `Security ${securityId} doesn't exist (not a file).`,
},
},
null,
......@@ -43,5 +43,5 @@ export function get(req, res) {
res.writeHead(200, {
"Content-Type": "application/json",
})
res.end(fs.readFileSync(stockFilePath))
res.end(fs.readFileSync(securityFilePath))
}
......@@ -28,10 +28,10 @@
{/if}
</li>
<li class="nav-item">
{#if child.segment === "stocks"}
<a class="nav-link active" href="/issuers/{issuer.id}/stocks" rel="prefetch">Stocks <span class="sr-only">(current)</span></a>
{#if child.segment === "securities"}
<a class="nav-link active" href="/issuers/{issuer.id}/securities" rel="prefetch">Securities <span class="sr-only">(current)</span></a>
{:else}
<a class="nav-link" href="/issuers/{issuer.id}/stocks" rel="prefetch">Stocks</a>
<a class="nav-link" href="/issuers/{issuer.id}/securities" rel="prefetch">Securities</a>
{/if}
</li>
<li class="nav-item">
......
......@@ -50,15 +50,15 @@
</div>
{/if}
<!-- {#if issuer.stocks}
<!-- {#if issuer.securities}
<div class="card my-3">
<div class="card-header">
<h3 class="mb-0">
<button class="btn btn-light" type="button">
{#if Object.keys(issuer.stocks).length > 1}
{Object.keys(issuer.stocks).length} stocks
{#if Object.keys(issuer.securities).length > 1}
{Object.keys(issuer.securities).length} securities
{:else}
{Object.keys(issuer.stocks).length} stock
{Object.keys(issuer.securities).length} stock
{/if}
</button>
</h3>
......
<p class="my-4 text-muted">
{#if Object.keys(issuer.securities).length > 1}
{Object.keys(issuer.securities).length} results
{:else}
{Object.keys(issuer.securities).length} result
{/if}
</p>
<div class="list-group">
{#each Object.values(issuer.securities) as security (security.id)}
<a class="list-group-item list-group-item-action" href="/securities/{security.id}">
<div>{(security.name || "Titre sans nom").toUpperCase()}</div>
<div class="text-muted">{security.share_type.name}</div>
<div class="text-muted">{[...new Set(security.types.map(securityType => securityType.name))].sort().join(", ")}</div>
</a>
{/each}
</div>
<p class="my-4 text-muted">
{#if Object.keys(issuer.stocks).length > 1}
{Object.keys(issuer.stocks).length} results
{:else}
{Object.keys(issuer.stocks).length} result
{/if}
</p>
<div class="list-group">
{#each Object.values(issuer.stocks) as stock (stock.id)}
<a class="list-group-item list-group-item-action" href="/stocks/{stock.id}">
<div>{(stock.name || "Titre sans nom").toUpperCase()}</div>
<div class="text-muted">{stock.share_type.name}</div>
<div class="text-muted">{[...new Set(stock.types.map(stockType => stockType.name))].sort().join(", ")}</div>
</a>
{/each}
</div>
<svelte:head>
<title>{stock.name} | Stocks | Eurhisfirm</title>
<title>{security.name} | Securities | Eurhisfirm</title>
</svelte:head>
<main class="container-fluid">
<h1>
<span class="text-muted">Stock</span> {stock.name}
<span class="text-muted">Security</span> {security.name}
<small class="text-muted">
{stock.share_type.name}
{security.share_type.name}
/
{[...new Set(stock.types.map(stockType => stockType.name))].sort().join(", ")}
{[...new Set(security.types.map(securityType => securityType.name))].sort().join(", ")}
</small>
</h1>
......@@ -32,35 +32,35 @@
<ul class="nav nav-tabs">
<li class="nav-item">
{#if child.segment === undefined}
<a class="nav-link active" href="/stocks/{stock.id}" rel="prefetch">Summary <span class="sr-only">(current)</span></a>
<a class="nav-link active" href="/securities/{security.id}" rel="prefetch">Summary <span class="sr-only">(current)</span></a>
{:else}
<a class="nav-link" href="/stocks/{stock.id}" rel="prefetch">Summary</a>
<a class="nav-link" href="/securities/{security.id}" rel="prefetch">Summary</a>
{/if}
</li>
<li class="nav-item">
{#if child.segment === "dividends"}
<a class="nav-link active" href="/stocks/{stock.id}/dividends" rel="prefetch">Dividends <span class="sr-only">(current)</span></a>
<a class="nav-link active" href="/securities/{security.id}/dividends" rel="prefetch">Dividends <span class="sr-only">(current)</span></a>
{:else}
<a class="nav-link" href="/stocks/{stock.id}/dividends" rel="prefetch">Dividends</a>
<a class="nav-link" href="/securities/{security.id}/dividends" rel="prefetch">Dividends</a>
{/if}
</li>
<li class="nav-item">
{#if child.segment === "prices"}
<a class="nav-link active" href="/stocks/{stock.id}/prices" rel="prefetch">Prices <span class="sr-only">(current)</span></a>
<a class="nav-link active" href="/securities/{security.id}/prices" rel="prefetch">Prices <span class="sr-only">(current)</span></a>
{:else}
<a class="nav-link" href="/stocks/{stock.id}/prices" rel="prefetch">Prices</a>
<a class="nav-link" href="/securities/{security.id}/prices" rel="prefetch">Prices</a>
{/if}
</li>
</ul>
<svelte:component this={child.component} {...child.props} {stock} />
<svelte:component this={child.component} {...child.props} {security} />
</main>
<script>
export default {
computed: {
simplifiedIssuers({ stock }) {
const simplifiedIssuerByIdAndName = stock.issuers.reduce((accumulator, issuer) => {
simplifiedIssuers({ security }) {
const simplifiedIssuerByIdAndName = security.issuers.reduce((accumulator, issuer) => {
accumulator[`${issuer.name} ${issuer.id}`] = {
id: issuer.id,
name: issuer.name,
......@@ -71,8 +71,8 @@
},
},
async preload({ params /* , query */ }) {
const { stock: stockId } = params
const response = await this.fetch(`/api/static/stocks/${stockId}`)
const { security: securityId } = params
const response = await this.fetch(`/api/static/securities/${securityId}`)
const result =
response.ok || response.status === 400
? await response.json()
......@@ -80,7 +80,7 @@
if (result.error) {
return this.error(result.error.code, result.error.message)
}
return { stock: result }
return { security: result }
},
}
</script>
\ No newline at end of file
<svelte:head>
<title>Dividends | {stock.name} | Stocks | Eurhisfirm</title>
<title>Dividends | {security.name} | Securities | Eurhisfirm</title>
</svelte:head>
TODO
{#each Object.values(stock.stock_exchanges || {}).sort(stockExchange => stockExchange.name) as stockExchange (`stock_exchange_${stockExchange.id}`)}
{#each Object.values(security.stock_exchanges || {}).sort(stockExchange => stockExchange.name) as stockExchange (`stock_exchange_${stockExchange.id}`)}
<div class="card my-3">
<div class="card-header">
Listed on <i>{stockExchange.name}</i>
......
<svelte:head>
<title>Prices | {stock.name} | Stocks | Eurhisfirm</title>
<title>Prices | {security.name} | Securities | Eurhisfirm</title>
</svelte:head>
<svelte:window on:resize="resize()"/>
......@@ -112,8 +112,8 @@
)
},
async preload({ params /* query */ }) {
const { stock: stockId } = params
const response = await this.fetch(`/api/stocks/${stockId}/prices`)
const { security: securityId } = params
const response = await this.fetch(`/api/securities/${securityId}/prices`)
const result =
response.ok || response.status === 400
? await response.json()
......
<svelte:head>
<title>Prices | {stock.name} | Stocks | Eurhisfirm</title>
<title>Prices | {security.name} | Securities | Eurhisfirm</title>
</svelte:head>
<div id="container" style="height:800px; width:100%;"></div>
......@@ -112,8 +112,8 @@
})
},
async preload({ params /* query */ }) {
const { stock: stockId } = params
const response = await this.fetch(`/api/stocks/${stockId}/prices`)
const { security: securityId } = params
const response = await this.fetch(`/api/securities/${securityId}/prices`)
const result =
response.ok || response.status === 400
? await response.json()
......
......@@ -10,7 +10,7 @@ import { cleanUpLine, objectsFromSqlResult } from "../model"
const dataDir = "data"
let pool = null
async function generateStocks() {
async function generateSecurities() {
const connection = await pool.getConnection()
try {
console.log("Reading stock exchanges...")
......@@ -44,8 +44,8 @@ async function generateStocks() {
}
}
console.log("Loading stock types...")
const stockTypeById = {}
console.log("Loading security types...")
const securityTypeById = {}
{
const entries = objectsFromSqlResult(
await connection.execute(
......@@ -59,22 +59,22 @@ async function generateStocks() {
)
)
for (let entry of entries) {
stockTypeById[entry.id] = entry
securityTypeById[entry.id] = entry
}
}
const stocksDir = path.join(dataDir, "stocks")
if (!fs.existsSync(stocksDir)) {
fs.mkdirSync(stocksDir)
const securitiesDir = path.join(dataDir, "securities")
if (!fs.existsSync(securitiesDir)) {
fs.mkdirSync(securitiesDir)
}
const existingStocksFilenames = new Set(
fs.readdirSync(stocksDir).filter(filename => /^stock_[0-9]+\.json$/.test(filename))
const existingSecuritiesFilenames = new Set(
fs.readdirSync(securitiesDir).filter(filename => /^security_[0-9]+\.json$/.test(filename))
)
console.log("Loading stocks...")
console.log("Loading securities...")
const issuersDir = path.join(dataDir, "issuers")
assert(fs.existsSync(issuersDir), `Issuers directory not found: ${issuersDir}`)
const stocks = objectsFromSqlResult(
const securities = objectsFromSqlResult(
await connection.execute(
`
select
......@@ -85,18 +85,19 @@ async function generateStocks() {
`
)
)
const stocksIds = new Set()
const securitiesIds = new Set()
const summaryByIssuerId = {}
for (let stock of stocks) {
// console.log(`Loading stock ${stock.id}...`)
stocksIds.add(stock.id)
for (let security of securities) {
// console.log(`Loading security ${security.id}...`)
securitiesIds.add(security.id)
stock.share_type = shareTypeById[stock.sharetype]
assert(stock.share_type !== undefined, `Unknown share type "${stock.share_type}" for stock ${stock.id}`)
delete stock.sharetype
security.share_type = shareTypeById[security.sharetype]
assert(security.share_type !== undefined, `Unknown share type "${security.share_type}" for security ${
security.id}`)
delete security.sharetype
{
// Add issuers to stock.
// Add issuers to security.
const entries = objectsFromSqlResult(
await connection.execute(
`
......@@ -105,14 +106,14 @@ async function generateStocks() {
to_char(sc.startdate, 'YYYY-MM-DD') as start_date,
to_char(sc.enddate, 'YYYY-MM-DD') as end_date
from stock_corporation sc
where sc.stock = :stockId
where sc.stock = :securityId
`,
{
stockId: { dir: oracledb.BIND_IN, val: stock.id, type: oracledb.INTEGER },
securityId: { dir: oracledb.BIND_IN, val: security.id, type: oracledb.INTEGER },
}
)
)
stock.issuers = entries
security.issuers = entries
.map(entry => {
const issuerFilePath = path.join(issuersDir, `issuer_${entry.issuer_id}.json`)
try {
......@@ -124,7 +125,7 @@ async function generateStocks() {
name: issuer.name,
}
} catch (e) {
console.log(`Unknown issuer ${entry.issuer_id} for stock ${stock.id}: ${e}`)
console.log(`Unknown issuer ${entry.issuer_id} for security ${security.id}: ${e}`)
return null
}
})
......@@ -132,7 +133,7 @@ async function generateStocks() {
}
{
// Add types to stock.
// Add types to security.
const entries = objectsFromSqlResult(
await connection.execute(
`
......@@ -141,25 +142,25 @@ async function generateStocks() {
to_char(st.startdate, 'YYYY-MM-DD') as start_date,
to_char(st.enddate, 'YYYY-MM-DD') as end_date
from stock_type st
where st.stock = :stockId
where st.stock = :securityId
`,
{
stockId: { dir: oracledb.BIND_IN, val: stock.id, type: oracledb.INTEGER },
securityId: { dir: oracledb.BIND_IN, val: security.id, type: oracledb.INTEGER },
}
)
)
stock.types = entries.map(entry => {
const stockType = stockTypeById[entry.type]
assert(stockType !== undefined, `Unknown stock type "${entry.type}" for stock ${stock.id}`)
security.types = entries.map(entry => {
const securityType = securityTypeById[entry.type]