begin move to postgres
parent
d6445cd239
commit
b496a35a27
@ -1,124 +0,0 @@
|
|||||||
import fs from 'fs/promises';
|
|
||||||
import duckdb from 'duckdb';
|
|
||||||
const db = new duckdb.Database('quotes.duckdb'); // or a file name for a persistent DB
|
|
||||||
|
|
||||||
const pathToCsvs = "/home/brian/Downloads/options-data";
|
|
||||||
|
|
||||||
const statements = [
|
|
||||||
"CREATE TYPE OPTION_TYPE as ENUM ('put', 'call');",
|
|
||||||
"CREATE TYPE OPTION_STYLE as ENUM ('A', 'E');",
|
|
||||||
`CREATE TABLE IF NOT EXISTS option_quote (
|
|
||||||
contract VARCHAR GENERATED ALWAYS AS (
|
|
||||||
CONCAT(
|
|
||||||
underlying ,
|
|
||||||
RIGHT(YEAR(expiration)::VARCHAR,2) ,
|
|
||||||
LPAD(MONTH(expiration)::VARCHAR,2,'0') ,
|
|
||||||
LPAD(DAY(expiration)::VARCHAR,2,'0') ,
|
|
||||||
(CASE WHEN type = 'call' THEN 'C' ELSE 'P' END) ,
|
|
||||||
LPAD(((strike*1000)::INTEGER)::VARCHAR,8,'0')
|
|
||||||
)
|
|
||||||
) VIRTUAL,
|
|
||||||
underlying VARCHAR,
|
|
||||||
expiration DATE,
|
|
||||||
type OPTION_TYPE,
|
|
||||||
strike FLOAT,
|
|
||||||
style OPTION_STYLE,
|
|
||||||
bid FLOAT,
|
|
||||||
bid_size INTEGER DEFAULT 0,
|
|
||||||
ask FLOAT,
|
|
||||||
ask_size INTEGER DEFAULT 0,
|
|
||||||
volume INTEGER,
|
|
||||||
open_interest INTEGER,
|
|
||||||
quote_date DATE,
|
|
||||||
delta FLOAT,
|
|
||||||
gamma FLOAT,
|
|
||||||
theta FLOAT,
|
|
||||||
vega FLOAT,
|
|
||||||
implied_volatility FLOAT
|
|
||||||
);`,
|
|
||||||
`CREATE TABLE IF NOT EXISTS stock_quote (
|
|
||||||
quote_date DATE,
|
|
||||||
symbol VARCHAR,
|
|
||||||
open FLOAT DEFAULT 0.0,
|
|
||||||
high FLOAT DEFAULT 0.0,
|
|
||||||
low FLOAT DEFAULT 0.0,
|
|
||||||
close FLOAT DEFAULT 0.0,
|
|
||||||
volume FLOAT DEFAULT 0.0,
|
|
||||||
adjust_close FLOAT DEFAULT 0.0
|
|
||||||
);`
|
|
||||||
];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const files = await fs.readdir(pathToCsvs);
|
|
||||||
for (const filename of files){
|
|
||||||
const fileExtension = filename.substring(filename.length-11);
|
|
||||||
if(fileExtension === 'options.csv' || fileExtension === 'options.cvs'){
|
|
||||||
const quoteDate = filename.substring(0,10);
|
|
||||||
statements.push(`INSERT INTO option_quote (
|
|
||||||
SELECT
|
|
||||||
underlying,
|
|
||||||
expiration,
|
|
||||||
type,
|
|
||||||
strike,
|
|
||||||
style,
|
|
||||||
bid,
|
|
||||||
bid_size,
|
|
||||||
ask,
|
|
||||||
ask_size,
|
|
||||||
volume,
|
|
||||||
open_interest,
|
|
||||||
quote_date,
|
|
||||||
delta,
|
|
||||||
gamma,
|
|
||||||
theta,
|
|
||||||
vega,
|
|
||||||
implied_volatility
|
|
||||||
FROM read_csv_auto('${pathToCsvs}/${filename}')
|
|
||||||
);`);
|
|
||||||
statements.push(`INSERT INTO stock_quote (
|
|
||||||
SELECT
|
|
||||||
'${quoteDate}',
|
|
||||||
symbol,
|
|
||||||
open,
|
|
||||||
high,
|
|
||||||
low,
|
|
||||||
close,
|
|
||||||
volume,
|
|
||||||
adjust_close
|
|
||||||
FROM read_csv_auto('${pathToCsvs}/${quoteDate}stocks.cvs')
|
|
||||||
);`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(statements);
|
|
||||||
db.exec(statements.join(' '), (err)=>{
|
|
||||||
if(err){
|
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
db.all("SELECT contract FROM option_quote WHERE underlying = 'TSLA' LIMIT 10", (err, res)=>{
|
|
||||||
if(err){
|
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(res[0])
|
|
||||||
});
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
db.run(`CREATE TABLE option_quote AS SELECT * FROM read_csv_auto('${filename}')`, (err)=>{
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
db.run(``);
|
|
||||||
db.all("SELECT count(*) AS count FROM prices WHERE underlying = 'TSLA'", function(err, res) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
console.log(res[0].count)
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
*/
|
|
@ -1,28 +1,7 @@
|
|||||||
import { join, dirname } from 'node:path'
|
import { ingestOptions, ingestStocks, sql } from './ingest';
|
||||||
import { fileURLToPath } from 'node:url'
|
|
||||||
import fs from 'node:fs/promises';
|
|
||||||
|
|
||||||
import { Low } from 'lowdb'
|
const sourceDataDir = '/home/avraham/programming/calendar-optimizer-csv';
|
||||||
import { JSONFile } from 'lowdb/node'
|
//await ingestStocks(sourceDataDir);
|
||||||
|
await ingestOptions(sourceDataDir);
|
||||||
|
|
||||||
import {parse} from 'csv-parse/sync';
|
await sql.end({timeout:60});
|
||||||
|
|
||||||
|
|
||||||
// initialize lowdb:
|
|
||||||
// db.json file path
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
||||||
const file = join(__dirname, 'db.json')
|
|
||||||
|
|
||||||
// Configure lowdb to write data to JSON file
|
|
||||||
const adapter = new JSONFile<{ stocks: Array<any>, options: Array<any> }>(file)
|
|
||||||
const defaultData = { stocks: [], options: [] }
|
|
||||||
const db = new Low(adapter, defaultData)
|
|
||||||
|
|
||||||
// read each csv, and ingest each row into lowdb
|
|
||||||
const csvDir = '/home/avraham/programming/calendar-optimizer-csv/';
|
|
||||||
const csvFiles = await fs.readdir(csvDir);
|
|
||||||
await Promise.all(csvFiles.filter((csvFile)=>csvFile.substring(10,16)==='stocks').map(async (csvFile)=>{
|
|
||||||
db.data.stocks.push( parse(await fs.readFile(join(csvDir, csvFile))) );
|
|
||||||
}));
|
|
||||||
|
|
||||||
await db.write();
|
|
@ -0,0 +1,172 @@
|
|||||||
|
import { join, dirname } from 'node:path'
|
||||||
|
import fs from 'node:fs/promises';
|
||||||
|
|
||||||
|
import postgres from 'postgres';
|
||||||
|
|
||||||
|
import {parse} from 'csv-parse/sync';
|
||||||
|
|
||||||
|
export const sql = postgres({
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: 5432,
|
||||||
|
user: 'postgres',
|
||||||
|
password: 'buginoo'
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sql`CREATE TYPE OPTION_TYPE as ENUM ('put', 'call');`;
|
||||||
|
await sql`CREATE TYPE OPTION_STYLE as ENUM ('A', 'E');`;
|
||||||
|
}
|
||||||
|
catch(err){}
|
||||||
|
|
||||||
|
/*
|
||||||
|
contract VARCHAR(20) GENERATED ALWAYS AS (
|
||||||
|
CONCAT(
|
||||||
|
underlying ,
|
||||||
|
RIGHT(YEAR(expiration)::VARCHAR,2) ,
|
||||||
|
LPAD(MONTH(expiration)::VARCHAR,2,'0') ,
|
||||||
|
LPAD(DAY(expiration)::VARCHAR,2,'0') ,
|
||||||
|
(CASE WHEN type = 'call' THEN 'C' ELSE 'P' END) ,
|
||||||
|
LPAD(((strike*1000)::INTEGER)::VARCHAR,8,'0')
|
||||||
|
)
|
||||||
|
),
|
||||||
|
*/
|
||||||
|
await sql`CREATE TABLE IF NOT EXISTS option_quote (
|
||||||
|
underlying VARCHAR(4),
|
||||||
|
expiration DATE,
|
||||||
|
type OPTION_TYPE,
|
||||||
|
strike FLOAT,
|
||||||
|
style OPTION_STYLE,
|
||||||
|
bid FLOAT,
|
||||||
|
bid_size INTEGER DEFAULT 0,
|
||||||
|
ask FLOAT,
|
||||||
|
ask_size INTEGER DEFAULT 0,
|
||||||
|
volume INTEGER,
|
||||||
|
open_interest INTEGER,
|
||||||
|
quote_date DATE,
|
||||||
|
delta FLOAT,
|
||||||
|
gamma FLOAT,
|
||||||
|
theta FLOAT,
|
||||||
|
vega FLOAT,
|
||||||
|
implied_volatility FLOAT
|
||||||
|
);`;
|
||||||
|
await sql`CREATE TABLE IF NOT EXISTS stock_quote (
|
||||||
|
quote_date DATE,
|
||||||
|
symbol VARCHAR,
|
||||||
|
open FLOAT DEFAULT 0.0,
|
||||||
|
high FLOAT DEFAULT 0.0,
|
||||||
|
low FLOAT DEFAULT 0.0,
|
||||||
|
close FLOAT DEFAULT 0.0,
|
||||||
|
volume FLOAT DEFAULT 0.0,
|
||||||
|
adjust_close FLOAT DEFAULT 0.0
|
||||||
|
);`;
|
||||||
|
|
||||||
|
export async function ingestStocks(sourceDataDir:string):Promise<void>{
|
||||||
|
// read each csv, and ingest each row into postgres:
|
||||||
|
const csvFiles = await fs.readdir(sourceDataDir);
|
||||||
|
await Promise.all(csvFiles.filter((csvFile)=>csvFile.substring(10,16)==='stocks').map(async (csvFile)=>{
|
||||||
|
const quoteDate = csvFile.substring(0,10);
|
||||||
|
const rows = parse(await fs.readFile(join(sourceDataDir, csvFile)));
|
||||||
|
await Promise.all(rows.map(async ([symbol, open, high, low, close, volume, adjust_close])=>{
|
||||||
|
open = Number(open);
|
||||||
|
high = Number(high);
|
||||||
|
low = Number(low);
|
||||||
|
close = Number(close);
|
||||||
|
volume = Number(volume);
|
||||||
|
adjust_close = Number(adjust_close);
|
||||||
|
try{
|
||||||
|
await sql`insert into "stock_quote" (
|
||||||
|
quote_date,
|
||||||
|
symbol,
|
||||||
|
open,
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
close,
|
||||||
|
volume,
|
||||||
|
adjust_close
|
||||||
|
) values (
|
||||||
|
${quoteDate},
|
||||||
|
${symbol},
|
||||||
|
${open},
|
||||||
|
${high},
|
||||||
|
${low},
|
||||||
|
${close},
|
||||||
|
${volume},
|
||||||
|
${adjust_close || 0}
|
||||||
|
);`;
|
||||||
|
console.log(`${quoteDate} ${symbol}`);
|
||||||
|
}
|
||||||
|
catch(err){
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function ingestOptions(sourceDataDir:string):Promise<void>{
|
||||||
|
// read each csv, and ingest each row into postgres:
|
||||||
|
const csvFiles = await fs.readdir(sourceDataDir);
|
||||||
|
await Promise.all(csvFiles.filter((csvFile)=>csvFile.substring(10,17)==='options').map(async (csvFile)=>{
|
||||||
|
const quoteDate = csvFile.substring(0,10);
|
||||||
|
const rows = parse(await fs.readFile(join(sourceDataDir, csvFile)));
|
||||||
|
await Promise.all(rows.map(async ([underlying, expiration, type, strike, style, bid, bid_size, ask, ask_size, volume, open_interest, quote_date, delta, gamma, theta, vega, implied_volatility])=>{
|
||||||
|
expiration=Number(expiration);
|
||||||
|
strike=Number(strike);
|
||||||
|
bid=Number(bid);
|
||||||
|
bid_size=Number(bid_size);
|
||||||
|
ask=Number(ask);
|
||||||
|
ask_size=Number(ask_size);
|
||||||
|
volume=Number(volume);
|
||||||
|
open_interest=Number(open_interest);
|
||||||
|
quote_date=Number(quote_date)
|
||||||
|
delta=Number(delta);
|
||||||
|
gamma=Number(gamma);
|
||||||
|
theta=Number(theta);
|
||||||
|
vega=Number(vega);
|
||||||
|
implied_volatility=Number(implied_volatility);
|
||||||
|
try{
|
||||||
|
await sql`insert into "option_quote" (
|
||||||
|
underlying,
|
||||||
|
expiration,
|
||||||
|
type,
|
||||||
|
strike,
|
||||||
|
style,
|
||||||
|
bid,
|
||||||
|
bid_size,
|
||||||
|
ask,
|
||||||
|
ask_size,
|
||||||
|
volume,
|
||||||
|
open_interest,
|
||||||
|
quote_date,
|
||||||
|
delta,
|
||||||
|
gamma,
|
||||||
|
theta,
|
||||||
|
vega,
|
||||||
|
implied_volatility
|
||||||
|
) values (
|
||||||
|
${underlying},
|
||||||
|
${expiration},
|
||||||
|
${type},
|
||||||
|
${strike},
|
||||||
|
${style},
|
||||||
|
${bid},
|
||||||
|
${bid_size},
|
||||||
|
${ask},
|
||||||
|
${ask_size},
|
||||||
|
${volume},
|
||||||
|
${open_interest},
|
||||||
|
${quote_date},
|
||||||
|
${delta},
|
||||||
|
${gamma},
|
||||||
|
${theta},
|
||||||
|
${vega},
|
||||||
|
${implied_volatility}
|
||||||
|
);`;
|
||||||
|
console.log(`options ${quoteDate} ${underlying}`);
|
||||||
|
}
|
||||||
|
catch(err){
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
docker run --rm \
|
||||||
|
--name 'postgres-calendar-optimizer' \
|
||||||
|
--network host \
|
||||||
|
-e POSTGRES_PASSWORD=buginoo \
|
||||||
|
-v "${PWD}/postgres:/var/lib/postgresql/data" \
|
||||||
|
postgres:15.3-bullseye
|
Loading…
Reference in New Issue