Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
34708e7
added a component interface and a powerpoint component with scaling c…
jeffreywallphd Dec 12, 2025
61ff7fe
fixed slideshow resizing problem
jeffreywallphd Dec 12, 2025
7121471
changed gitignore
jeffreywallphd Dec 13, 2025
fb169ce
added new gateway and main and preload functionality for neo4j graph …
jeffreywallphd Dec 13, 2025
552c2ed
changes to gitignore
jeffreywallphd Dec 13, 2025
19144d0
added new sidecar initializer to initialize neo4j graphs
jeffreywallphd Dec 14, 2025
c5d80be
added jre-win to .gitignore
jeffreywallphd Dec 14, 2025
e8ca9ff
fixed multiple neo4j write query problem. Neo4j now sets up database
jeffreywallphd Dec 14, 2025
27b4a9b
updated to check for graph exists to avoid overwriting initialized gr…
jeffreywallphd Dec 14, 2025
02df51f
added status messages to initializer and bundled slideshows into appdata
jeffreywallphd Dec 14, 2025
bbe5ec5
added default thumbnail for news when news thumbnail fails
jeffreywallphd Dec 14, 2025
559fc3d
creating new portfolio risk analysis page
jeffreywallphd Dec 15, 2025
7c49040
updating Risk Analysis page. Creating returns time series
jeffreywallphd Dec 15, 2025
6cb7336
added calculations for portfolio volatility
jeffreywallphd Dec 16, 2025
b275618
updated view to account for calculating time
jeffreywallphd Dec 16, 2025
2663288
added portfolio risk table with daily and annual volatility, max draw…
jeffreywallphd Dec 17, 2025
38b8836
updated portfolio with popover term descriptions and add sleep calls …
jeffreywallphd Dec 17, 2025
eb940c2
updated loaders and on change events. Fixed trading to disallow purch…
jeffreywallphd Dec 18, 2025
af68eb7
Merge branch 'main' into hfAdapter
jdwallMTU Jan 5, 2026
18772eb
Made changes to Main.js to make software cross platform compatible
JabobleHead Feb 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
/public
/dist
/.webpack
/open-fin-al/resources/neo4j-win/
/open-fin-al/resources/jre-win/

# misc
.DS_Store
Expand Down
3 changes: 3 additions & 0 deletions open-fin-al/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/jest": "^30.0.0",
"@types/react": "^19.2.7",
"@vercel/webpack-asset-relocator-loader": "^1.7.3",
"babel-loader": "^10.0.0",
"browserify-fs": "^1.0.0",
Expand Down Expand Up @@ -72,6 +73,7 @@
"cors": "^2.8.5",
"crypto": "^1.0.1",
"crypto-browserify": "^3.12.1",
"danfojs": "^1.2.0",
"electron-squirrel-startup": "^1.0.1",
"events": "^3.3.0",
"express": "^5.1.0",
Expand All @@ -80,6 +82,7 @@
"http": "^0.0.1-security",
"https": "^1.0.0",
"keytar": "^7.9.0",
"neo4j-driver": "^6.0.1",
"node-html-parser": "^7.0.1",
"os-browserify": "^0.3.0",
"pptx-preview": "^1.0.7",
Expand Down
3 changes: 3 additions & 0 deletions open-fin-al/resources/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Purpose of this Folder
The resources folder should contain the following folders:
neo4j-win - this folder should include binaries for neo4j community for Windows
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ export class AlphaVantageEconomicGateway implements IKeyedDataGateway {
throw new Error("This gateway does not have the ability to post content");
}

async read(entity: IEntity, action: string): Promise<Array<IEntity>> {
async read(entity: IEntity, action: string): Promise<Array<IEntity>> {
const sleep = (ms:number) => new Promise(resolve => setTimeout(resolve, ms));
await sleep(1001);

var url = `${this.baseURL}?`;

if(action==="getGDP") {
Expand Down
14 changes: 14 additions & 0 deletions open-fin-al/src/Gateway/Data/ICredentialedDataGateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {IEntity} from "../../Entity/IEntity";
import { IDataGateway } from "./IDataGateway";

export interface ICredentialedDataGateway extends IDataGateway {
user: string;
key: string;
sourceName: string;
connect(): void;
disconnect(): void;
create(entity: IEntity, action?: string): Promise<Boolean>;
read(entity: IEntity, action?: string): Promise<Array<IEntity>>;
update(entity: IEntity, action?: string): Promise<number>;
delete(entity: IEntity, action?: string): Promise<number>;
}
1 change: 1 addition & 0 deletions open-fin-al/src/Gateway/Data/IDataGateway.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {IEntity} from "../../Entity/IEntity";

export interface IDataGateway {
user?: string;
key?: string;
sourceName: string;
connect(): void;
Expand Down
4 changes: 2 additions & 2 deletions open-fin-al/src/Gateway/Data/IKeylessDataGateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { IDataGateway } from "./IDataGateway";

export interface IKeylessDataGateway extends IDataGateway {
sourceName: string;
connect(): void;
disconnect(): void;
connect(): void | Promise<void> | Promise<boolean>;
disconnect(): void | Promise<void> | Promise<boolean>;
create(entity: IEntity, action?: string): Promise<Boolean>;
read(entity: IEntity, action?: string): Promise<Array<IEntity>>;
update(entity: IEntity, action?: string): Promise<number>;
Expand Down
17 changes: 17 additions & 0 deletions open-fin-al/src/Gateway/Data/INeo4JGraphGateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {IEntity} from "../../Entity/IEntity";
import { IDataGateway } from "./IDataGateway";

export interface INeo4JGraphGateway extends IDataGateway {
user: string;
key: string;
sourceName: string;
connect(): Promise<boolean>;
disconnect(): Promise<boolean>;
create(entity: IEntity, action?: string): Promise<Boolean>;
read(entity: IEntity, action?: string): Promise<Array<IEntity>>;
update(entity: IEntity, action?: string): Promise<number>;
delete(entity: IEntity, action?: string): Promise<number>;
checkGraphConnected(): Promise<Boolean>;
checkGraphExists(): Promise<Boolean>;
checkLastGraphUpdate() : Promise<Date>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export class AlphaVantageMarketGateway implements IKeyedDataGateway {
}

async read(entity: IEntity, action: string): Promise<Array<IEntity>> {
const sleep = (ms:number) => new Promise(resolve => setTimeout(resolve, ms));
await sleep(1001);

var url = `${this.baseURL}?function=MARKET_STATUS&apikey=${entity.getFieldValue("key")}`;
const urlObject = new URL(url);

Expand Down
211 changes: 211 additions & 0 deletions open-fin-al/src/Gateway/Data/Neo4J/Neo4JGraphCreationGateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
import neo4j, {Record, Node} from "neo4j-driver";
import { INeo4JGraphGateway } from "../INeo4JGraphGateway";
import { IEntity } from "@Entity/IEntity";

declare global {
interface Window {
neo4j: any
}
}

export class Neo4JGraphCreationGateway implements INeo4JGraphGateway {
user: string = "";
key: string = "";
connection: any = null;
sourceName: string = "Neo4j Local Database";

async connect(): Promise<boolean> {
try {
return await window.neo4j.start();
} catch (error) {
console.error("Error connecting to Neo4j server:", error);
return false;
}
}

async disconnect(): Promise<boolean> {
try {
return await window.neo4j.stop();
} catch (error) {
console.error("Error disconnecting from Neo4j server:", error);
return false;
}
}

// used to create and periodically refresh the cache
async create(): Promise<Boolean> {
const schema: string[] = [
`CREATE CONSTRAINT user_userId_unique IF NOT EXISTS
FOR (u:User)
REQUIRE u.userId IS UNIQUE`,

`CREATE CONSTRAINT module_moduleId_unique IF NOT EXISTS
FOR (m:Module)
REQUIRE m.moduleId IS UNIQUE`,

`CREATE CONSTRAINT concept_conceptId_unique IF NOT EXISTS
FOR (c:Concept)
REQUIRE c.conceptId IS UNIQUE`,

`CREATE INDEX module_minLevel IF NOT EXISTS
FOR (m:Module) ON (m.minLevel)`,

`CREATE INDEX module_riskTag IF NOT EXISTS
FOR (m:Module) ON (m.riskTag)`,
];

const merges: string[] = [
`MERGE (u1:User { userId: "u_alice" })
SET u1.overallLevel = 2,
u1.riskScore = 2`,

`MERGE (u2:User { userId: "u_bob" })
SET u2.overallLevel = 1,
u2.riskScore = 1`,

`MERGE (c_etf:Concept { conceptId: "c_etf", name: "ETFs" })`,
`MERGE (c_div:Concept { conceptId: "c_div", name: "Dividends" })`,
`MERGE (c_opt:Concept { conceptId: "c_opt", name: "Options" })`,
`MERGE (c_cc:Concept { conceptId: "c_cc", name: "Covered Calls" })`,
`MERGE (c_risk:Concept{ conceptId: "c_risk",name: "Risk Management" })`,

`MERGE (m1:Module { moduleId: "m_etf_basics" })
SET m1.title = "ETF Basics",
m1.description = "Introduction to exchange-traded funds",
m1.timeEstimate = 45,
m1.minLevel = 1,
m1.riskTag = 1`,

`MERGE (m2:Module { moduleId: "m_div_income" })
SET m2.title = "Dividend Investing",
m2.description = "Building income with dividends",
m2.timeEstimate = 60,
m2.minLevel = 2,
m2.riskTag = 1`,

`MERGE (m3:Module { moduleId: "m_options_101" })
SET m3.title = "Options 101",
m3.description = "Foundations of options trading",
m3.timeEstimate = 90,
m3.minLevel = 3,
m3.riskTag = 3`,

`MERGE (m4:Module { moduleId: "m_covered_calls" })
SET m4.title = "Covered Call Strategies",
m4.description = "Generating income with covered calls",
m4.timeEstimate = 75,
m4.minLevel = 3,
m4.riskTag = 2`,

`MATCH (m1:Module {moduleId:"m_etf_basics"}),
(c_etf:Concept {conceptId:"c_etf"})
MERGE (m1)-[:TEACHES {weight: 1.0}]->(c_etf)`,

`MATCH (m2:Module {moduleId:"m_div_income"}),
(c_div:Concept {conceptId:"c_div"}),
(c_etf:Concept {conceptId:"c_etf"})
MERGE (m2)-[:TEACHES {weight: 0.7}]->(c_div)
MERGE (m2)-[:TEACHES {weight: 0.3}]->(c_etf)`,

`MATCH (m3:Module {moduleId:"m_options_101"}),
(c_opt:Concept {conceptId:"c_opt"}),
(c_risk:Concept {conceptId:"c_risk"})
MERGE (m3)-[:TEACHES {weight: 0.7}]->(c_opt)
MERGE (m3)-[:TEACHES {weight: 0.3}]->(c_risk)`,

`MATCH (m4:Module {moduleId:"m_covered_calls"}),
(c_cc:Concept {conceptId:"c_cc"}),
(c_opt:Concept {conceptId:"c_opt"})
MERGE (m4)-[:TEACHES {weight: 0.6}]->(c_cc)
MERGE (m4)-[:TEACHES {weight: 0.4}]->(c_opt)`,

`MATCH (m2:Module {moduleId:"m_div_income"}),
(m1:Module {moduleId:"m_etf_basics"})
MERGE (m2)-[:REQUIRES {strict:true}]->(m1)`,

`MATCH (m3:Module {moduleId:"m_options_101"}),
(m1:Module {moduleId:"m_etf_basics"})
MERGE (m3)-[:REQUIRES {strict:true}]->(m1)`,

`MATCH (m4:Module {moduleId:"m_covered_calls"}),
(m3:Module {moduleId:"m_options_101"})
MERGE (m4)-[:REQUIRES {strict:true}]->(m3)`,

`MATCH (u:User {userId:"u_alice"}),
(c_etf:Concept {conceptId:"c_etf"}),
(c_div:Concept {conceptId:"c_div"})
MERGE (u)-[:KNOWS {level:2, confidence:0.8, updatedAt:datetime()}]->(c_etf)
MERGE (u)-[:KNOWS {level:1, confidence:0.6, updatedAt:datetime()}]->(c_div)`,

`MATCH (u:User {userId:"u_bob"}),
(c_etf:Concept {conceptId:"c_etf"})
MERGE (u)-[:KNOWS {level:1, confidence:0.5, updatedAt:datetime()}]->(c_etf)`,

`MATCH (u:User {userId:"u_alice"}),
(m1:Module {moduleId:"m_etf_basics"})
MERGE (u)-[:COMPLETED {at:datetime()}]->(m1)`,

`MATCH (u:User {userId:"u_bob"}),
(m1:Module {moduleId:"m_etf_basics"})
MERGE (u)-[:COMPLETED {at:datetime()}]->(m1)`,
];

try {
await window.neo4j.executeQuery("write", schema);
await window.neo4j.executeQuery("write", merges);
return true;
} catch(error) {
window.console.error("Error creating graph schema/data in Neo4j server:", error);
return false;
}
}

async read(): Promise<IEntity[]> {
throw new Error("This gatweay does not allow for reading. This gateway is designed for posting only.");
}

update(entity: IEntity, action: string): Promise<number> {
throw new Error("This gateway does not have the ability to update content. Updates are handled by periodically deleting and re-entering data");
}

async delete(entity: IEntity, action: string): Promise<number> {
throw new Error("This gatweay does not allow deleting data. This gateway is designed for posting only.");
}

//check to see if the database exists
async checkGraphExists(): Promise<Boolean> {
try {
const query =
`MATCH (n)
RETURN count(n) AS count`
;

const result = await window.neo4j.executeQuery("read", query);
window.console.log(result);
const count = result.records[0].count;
window.console.log(count);
if(count > 0) {
return true;
} else {
return false;
}
} catch(error) {
window.console.log(error);
return false;
}
}

async checkGraphConnected():Promise<Boolean> {
try {
const isConnected = await window.neo4j.isConnected();
return isConnected;
} catch(error) {
return false;
}
}

//for database tables that act as cache, check for the last time a table was updated
async checkLastGraphUpdate():Promise<any> {
throw new Error("This gatweay does not allow for checking tables. This gateway is designed for posting only.");
}
}
66 changes: 66 additions & 0 deletions open-fin-al/src/Gateway/Data/Neo4J/Neo4JGraphGateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import neo4j, {Record, Node} from "neo4j-driver";
import {IEntity} from "../../../Entity/IEntity";
import {ICredentialedDataGateway} from "../ICredentialedDataGateway";

declare global {
interface Window {
neo4j: any
}
}

export class Neo4JGraphGateway implements ICredentialedDataGateway {
user: string = "";
key: string = "";
sourceName: string = "Neo4j Local Database";

async connect(): Promise<boolean> {
try {
return await window.neo4j.start();
} catch (error) {
console.error("Error connecting to Neo4j server:", error);
return false;
}
}

async disconnect(): Promise<boolean> {
try {
return await window.neo4j.stop();
} catch (error) {
console.error("Error disconnecting from Neo4j server:", error);
return false;
}
}

create(entity: IEntity, action: string): Promise<Boolean> {
//at the moment, users will not be permitted to create new graphs
throw new Error("Method not implemented.");
}

async read(entity: IEntity, action: string): Promise<IEntity[]> {
const id:any = null;
try {
const query =
`
MATCH (u:User { id: $id })
RETURN u
`
;

const results = await window.neo4j.executeQuery(query, {id: id});
return results;
} catch (error) {
window.console.error("Error reading from Neo4j server:", error);
return null;
}
}

update(entity: IEntity, action: string): Promise<number> {
//at the moment, users will not be permitted to update graphs;
throw new Error("Method not implemented.");
}

delete(entity: IEntity, action: string): Promise<number> {
//at the moment, users will not be permitted to delete graphs
throw new Error("Method not implemented.");
}
}
Loading