Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
57 changes: 57 additions & 0 deletions .github/workflows/publish-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Publish the patched TradeNote image to this fork's GitHub Container Registry.
#
# Uses the built-in GITHUB_TOKEN, so no registry secret needs to be configured.
# Builds docker/Dockerfile — the full build, not a derived FROM-image — so the
# Vue frontend is compiled from patched source too. That matters: the server and
# the web UI each carry their own copy of the netting logic, and a derived image
# would patch only the server, leaving a hand-import through the browser still
# merging accounts.
name: publish-image

on:
push:
branches: [main, 'fix/**']
tags: ['v*']
workflow_dispatch:

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4

- uses: docker/setup-buildx-action@v3

- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- id: meta
uses: docker/metadata-action@v5
with:
# ghcr requires a lowercase path; github.repository is already lowercase here.
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=tag
type=sha,format=long
type=raw,value=latest,enable={{is_default_branch}}

- uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
10 changes: 7 additions & 3 deletions index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ console.log(' -> Database URI ' + hiddenDatabaseURI)
let tradenoteDatabase = process.env.TRADENOTE_DATABASE

var app = express();
app.use(express.json());
// Default body-parser limit is 100 kB. One account's busy day already runs ~92 kB
// compactly encoded, and a day CANNOT be split (dedupe omits whole days that
// already hold trades), so consolidating a second account into one user pushes
// ordinary days past the ceiling and they 413.
app.use(express.json({ limit: '25mb' }));

const port = process.env.TRADENOTE_PORT;
const PROXY_PORT = 39482;
Expand Down Expand Up @@ -339,7 +343,7 @@ const setupApiRoutes = (app) => {



app.use(express.json());
app.use(express.json({ limit: '25mb' }));

let allUsers
const getAllUsers = async () => {
Expand Down Expand Up @@ -498,7 +502,7 @@ const startIndex = async () => {
resolve();
} else {
// In production, handle API routes normally
app.use('/api/*', express.json(), (req, res, next) => {
app.use('/api/*', express.json({ limit: '25mb' }), (req, res, next) => {
//console.log(`Received API request: ${req.method} ${req.url}`);
next(); // Pass control to specific API handlers
});
Expand Down
30 changes: 23 additions & 7 deletions src/utils/addTrades.js
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ async function createTempExecutions() {
temp2.price = parseFloat(tradesData[key].Price);

temp2.execTime = dayjs.tz(formatedDateTD + " " + tradesData[key]['Exec Time'], timeZoneTrade.value).unix()
let tempId = "e" + temp2.execTime + "_" + temp2.symbol.replace(".", "_") + "_" + temp2.type + "_" + temp2.side;
let tempId = "e" + temp2.execTime + "_" + temp2.account + "_" + temp2.symbol.replace(".", "_") + "_" + temp2.type + "_" + temp2.side;
// It happens that two or more trades happen at the same (second) time. So we need to differentiated them
if (tempId != lastId) {
x = 1
Expand Down Expand Up @@ -938,7 +938,7 @@ async function createTrades() {
var b = _
.chain(tempExecutions)
.orderBy(["execTime"], ["asc"])
.groupBy(item => `"${item.symbol}+${item.type}+${item.strategy}+${item.td}"`);
.groupBy(item => `"${item.account}+${item.symbol}+${item.type}+${item.strategy}+${item.td}"`);

let objectB = JSON.parse(JSON.stringify(b))
//console.log("object b "+JSON.stringify(objectB))
Expand Down Expand Up @@ -987,10 +987,10 @@ async function createTrades() {


/* Checking existing open position amongst open positions stored IN PARSE / DATABASE */
const existingOpenPositionParseIndex = openPositionsParse.findIndex(x => x.symbol == tempExec.symbol && x.type == tempExec.type && x.strategy == tempExec.strategy)
const existingOpenPositionParseIndex = openPositionsParse.findIndex(x => x.account == tempExec.account && x.symbol == tempExec.symbol && x.type == tempExec.type && x.strategy == tempExec.strategy)

/* Checking existing open position amongst open positions stored LOCALLT */
const existingOpenPositionFileIndex = openPositionsFile.findIndex(x => x.symbol == tempExec.symbol && x.type == tempExec.type && x.strategy == tempExec.strategy)
const existingOpenPositionFileIndex = openPositionsFile.findIndex(x => x.account == tempExec.account && x.symbol == tempExec.symbol && x.type == tempExec.type && x.strategy == tempExec.strategy)

//checking existing open positions array when importing file
if (newTrade == true) {
Expand Down Expand Up @@ -1071,7 +1071,7 @@ async function createTrades() {
//console.log(" -> exec id "+tempExec.id)
//tempExecIds.push(tempExec.id)

temp7.id = tempExec.side == "B" || tempExec.side == "S" ? "t" + tempExec.execTime + "_" + tempExec.symbol + "_" + tempExec.type + "_B" : "t" + tempExec.execTime + "_" + tempExec.symbol + "_" + tempExec.type + "_SS"
temp7.id = tempExec.side == "B" || tempExec.side == "S" ? "t" + tempExec.execTime + "_" + tempExec.account + "_" + tempExec.symbol + "_" + tempExec.type + "_B" : "t" + tempExec.execTime + "_" + tempExec.account + "_" + tempExec.symbol + "_" + tempExec.type + "_SS"
console.log(" --> ID " + temp7.id)
currentTradeId = temp7.id
temp7.account = tempExec.account;
Expand Down Expand Up @@ -2101,6 +2101,16 @@ export async function useUploadTrades(param99, param0) {
const results = await query.first(param99 === "api" ? { useMasterKey: true } : undefined);
//console.log(" results "+JSON.stringify(results))
if (results) {
// Dedupe on write. The list is append-only and a journal that
// was imported before the fix above already carries repeats;
// without this it would keep saving them back.
const seenAccounts = new Set()
param = (param || []).filter(a => {
const v = a && a.value
if (v == null || seenAccounts.has(v)) return false
seenAccounts.add(v)
return true
})
results.set("accounts", param)
//console.log("param 2" + JSON.stringify(param2))
if (param99 === "api") {
Expand Down Expand Up @@ -2140,8 +2150,14 @@ export async function useUploadTrades(param99, param0) {
if (!check) {
let tempArray = currentUser.value.accounts
let temp = {}
temp.value = tradeAccounts[0]
temp.label = tradeAccounts[0]
// Was tradeAccounts[0], which is the account being
// iterated ONLY when there is one of them. With two
// accounts in a journal every import re-added the first
// and never added the second, so the filter accumulated
// one duplicate per import and the second account was
// never selectable.
temp.value = element
temp.label = element
tempArray.push(temp)
updateTradeAccounts(tempArray, temp.value)
}
Expand Down