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,7 +3,7 @@ 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
......@@ -68,11 +68,11 @@ export async function get(req, res) {
(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)
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
......@@ -98,10 +98,10 @@ export async function get(req, res) {
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
......@@ -115,11 +115,11 @@ export async function get(req, res) {
(
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)
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]
assert(securityType !== undefined, `Unknown security type "${entry.type}" for security ${security.id}`)
delete entry.type
return {
...entry,
...stockType,
...securityType,
}
})
}
// Add stock exchanges and notations to stock.
// Add security exchanges and notations to security.
const notations = objectsFromSqlResult(
await connection.execute(
`
......@@ -170,29 +171,30 @@ async function generateStocks() {
to_char(n.enddate, 'YYYY-MM-DD') as end_date,
n.stockexchange_id
from notation n
where n.stock = :stockId
where n.stock = :securityId
order by n.startdate, n.enddate
`,
{
stockId: { dir: oracledb.BIND_IN, val: stock.id, type: oracledb.INTEGER },
securityId: { dir: oracledb.BIND_IN, val: security.id, type: oracledb.INTEGER },
}
)
)
{
let stockExchangeByIdAtStock = {}
let stockExchangeByIdAtSecurity = {}
for (let notation of notations) {
if (notation.stockexchange_id === null) {
console.log(`Null stock exchange for notation ${notation.id} of stock ${stock.id}`)
console.log(`Null stock exchange for notation ${notation.id} of security ${security.id}`)
continue
}
let stockExchange = stockExchangeByIdAtStock[notation.stockexchange_id]
let stockExchange = stockExchangeByIdAtSecurity[notation.stockexchange_id]
if (stockExchange === undefined) {
stockExchange = stockExchangeById[notation.stockexchange_id]
assert(
stockExchange !== undefined,
`Unknown stock exchange "${notation.stockexchange_id}" for notation ${notation.id} of stock ${stock.id}`
`Unknown stock exchange "${notation.stockexchange_id}" for notation ${
notation.id} of security ${security.id}`
)
stockExchange = stockExchangeByIdAtStock[notation.stockexchange_id] = { ...stockExchange }
stockExchange = stockExchangeByIdAtSecurity[notation.stockexchange_id] = { ...stockExchange }
}
delete notation.stockexchange_id
......@@ -202,11 +204,11 @@ async function generateStocks() {
}
notationsAtStockExchange[notation.id] = notation
}
stock.stock_exchanges = stockExchangeByIdAtStock
security.stock_exchanges = stockExchangeByIdAtSecurity
}
{
// Add stock_name.name to stock notations names.
// Add stock_name.name to security notations names.
const entries = objectsFromSqlResult(
await connection.execute(
`
......@@ -216,20 +218,20 @@ async function generateStocks() {
to_char(sn.enddate, 'YYYY-MM-DD') as end_date,
sn.stockexchange
from stock_name sn
where sn.stock = :stockId
where sn.stock = :securityId
and sn.name <> 'STOCK_FICTIF'
`,
{
stockId: { dir: oracledb.BIND_IN, val: stock.id, type: oracledb.INTEGER },
securityId: { dir: oracledb.BIND_IN, val: security.id, type: oracledb.INTEGER },
}
)
)
for (let entry of entries) {
entry.name = cleanUpLine(entry.name)
const stockExchange = stock.stock_exchanges[entry.stockexchange]
const stockExchange = security.stock_exchanges[entry.stockexchange]
if (stockExchange === undefined) {
console.log(`Unknown stock exchange "${entry.stockexchange}" for stock name "${entry.name}" of stock ${
stock.id}`)
console.log(`Unknown stock exchange "${entry.stockexchange}" for security name "${
entry.name}" of security ${security.id}`)
continue
}
delete entry.stockexchange
......@@ -293,7 +295,7 @@ async function generateStocks() {
}
// Add stats on forward prices to each stock exchange.
for (let stockExchange of Object.values(stock.stock_exchanges)) {
for (let stockExchange of Object.values(security.stock_exchanges)) {
const entry = objectsFromSqlResult(
await connection.execute(
`
......@@ -302,13 +304,13 @@ async function generateStocks() {
to_char(max(day), 'YYYY-MM-DD') as end_date
from notation_price np
join notation n on n.id = np.notation
where n.stock = :stockId
where n.stock = :securityId
and np.market = 1
and n.stockexchange_id = :stockExchangeId
`,
{
stockExchangeId: { dir: oracledb.BIND_IN, val: stockExchange.id, type: oracledb.INTEGER },
stockId: { dir: oracledb.BIND_IN, val: stock.id, type: oracledb.INTEGER },
securityId: { dir: oracledb.BIND_IN, val: security.id, type: oracledb.INTEGER },
}
)
)[0] || null
......@@ -318,19 +320,19 @@ async function generateStocks() {
}
// Add stats on listings to each stock exchange.
for (let stockExchange of Object.values(stock.stock_exchanges)) {
for (let stockExchange of Object.values(security.stock_exchanges)) {
const entry = objectsFromSqlResult(
await connection.execute(
`
select
to_char(min(n.startdate), 'YYYY-MM-DD') as start_date
from notation n
where n.stock = :stockId
where n.stock = :securityId