Fix: Calculate P&L correctly for external closures
- Save currentSize before it becomes 0 in external closure detection - Use sizeBeforeClosure for P&L calculation instead of trade.currentSize - Prevents /bin/bash.00 P&L for TP2 exits when position closes externally - Ensures win/loss analytics counts TP trades correctly
This commit is contained in:
@@ -282,6 +282,9 @@ export class PositionManager {
|
||||
// Position closed externally (by on-chain TP/SL order)
|
||||
console.log(`⚠️ Position ${trade.symbol} was closed externally (by on-chain order)`)
|
||||
|
||||
// Save currentSize before it becomes 0
|
||||
const sizeBeforeClosure = trade.currentSize
|
||||
|
||||
// Determine exit reason based on price
|
||||
let exitReason: 'TP1' | 'TP2' | 'SL' | 'SOFT_SL' | 'HARD_SL' = 'SL'
|
||||
|
||||
@@ -304,14 +307,14 @@ export class PositionManager {
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate final P&L
|
||||
// Calculate final P&L using size BEFORE closure
|
||||
const profitPercent = this.calculateProfitPercent(
|
||||
trade.entryPrice,
|
||||
currentPrice,
|
||||
trade.direction
|
||||
)
|
||||
const accountPnL = profitPercent * trade.leverage
|
||||
const realizedPnL = (trade.currentSize * accountPnL) / 100
|
||||
const realizedPnL = (sizeBeforeClosure * accountPnL) / 100
|
||||
|
||||
// Update database
|
||||
const holdTimeSeconds = Math.floor((Date.now() - trade.entryTime) / 1000)
|
||||
|
||||
@@ -4,18 +4,18 @@
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "3371ad7c-0866-4161-90a4-f251de4aceb8",
|
||||
"path": "tradingview-bot-v4",
|
||||
"options": {}
|
||||
},
|
||||
"id": "35b54214-9761-49dc-97b6-df39543f0a7b",
|
||||
"id": "c762618c-fac7-4689-9356-8a78fc7160a8",
|
||||
"name": "Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-840,
|
||||
660
|
||||
-980,
|
||||
680
|
||||
],
|
||||
"webhookId": "3371ad7c-0866-4161-90a4-f251de4aceb8"
|
||||
"webhookId": "tradingview-bot-v4"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
@@ -27,27 +27,48 @@
|
||||
},
|
||||
{
|
||||
"name": "symbol",
|
||||
"stringValue": "={{ ($json.body || '').toString().match(/\\bSOL\\b/i) ? 'SOL-PERP' : (($json.body || '').toString().match(/\\bBTC\\b/i) ? 'BTC-PERP' : (($json.body || '').toString().match(/\\bETH\\b/i) ? 'ETH-PERP' : 'SOL-PERP')) }}"
|
||||
"stringValue": "={{ $json.body.match(/\\bSOL\\b/i) ? 'SOL-PERP' : ($json.body.match(/\\bBTC\\b/i) ? 'BTC-PERP' : ($json.body.match(/\\bETH\\b/i) ? 'ETH-PERP' : 'SOL-PERP')) }}"
|
||||
},
|
||||
{
|
||||
"name": "direction",
|
||||
"stringValue": "={{ ($json.body || '').toString().match(/\\b(sell|short)\\b/i) ? 'short' : 'long' }}"
|
||||
"stringValue": "={{ $json.body.match(/\\b(sell|short)\\b/i) ? 'short' : 'long' }}"
|
||||
},
|
||||
{
|
||||
"name": "timeframe",
|
||||
"stringValue": "5"
|
||||
"stringValue": "={{ $json.body.match(/\\.P\\s+(\\d+)/)?.[1] || '15' }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "99336995-2326-4575-9970-26afcf957132",
|
||||
"id": "97d5b0ad-d078-411f-8f34-c9a81d18d921",
|
||||
"name": "Parse Signal",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
-660,
|
||||
660
|
||||
-780,
|
||||
680
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"string": [
|
||||
{
|
||||
"value1": "={{ $json.timeframe }}",
|
||||
"operation": "equals",
|
||||
"value2": "15"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "2e0bf241-9fb6-40bd-89f6-2dceafe34ef9",
|
||||
"name": "15min Chart Only?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-560,
|
||||
540
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -74,12 +95,12 @@
|
||||
"jsonBody": "={\n \"symbol\": \"{{ $json.symbol }}\",\n \"direction\": \"{{ $json.direction }}\"\n}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "d42e7897-eadd-4202-8565-ac60759b46e1",
|
||||
"id": "c1165de4-2095-4f5f-b9b1-18e76fd8c47b",
|
||||
"name": "Check Risk",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4,
|
||||
"position": [
|
||||
-340,
|
||||
-280,
|
||||
660
|
||||
],
|
||||
"credentials": {
|
||||
@@ -100,12 +121,12 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "a60bfecb-d2f4-4165-a609-e6ed437aa2aa",
|
||||
"id": "b9fa2b47-2acd-4be0-9d50-3f0348e04ec6",
|
||||
"name": "Risk Passed?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-140,
|
||||
-80,
|
||||
660
|
||||
]
|
||||
},
|
||||
@@ -135,7 +156,7 @@
|
||||
"timeout": 120000
|
||||
}
|
||||
},
|
||||
"id": "95c46846-4b6a-4f9e-ad93-be223b73a618",
|
||||
"id": "c2ec5f8c-42d1-414f-bdd6-0a440bc8fea9",
|
||||
"name": "Execute Trade",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4,
|
||||
@@ -161,7 +182,7 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "18342642-e76f-484f-b532-d29846536a9c",
|
||||
"id": "16dbf434-a07c-4666-82f2-cdc8814fe216",
|
||||
"name": "Trade Success?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
@@ -182,7 +203,7 @@
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "9da40e3d-b855-4c65-a032-c6fcf88245d4",
|
||||
"id": "79ab6122-cbd3-4aac-97d7-6b54f64e29b5",
|
||||
"name": "Format Success",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
@@ -203,7 +224,7 @@
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "500751c7-21bb-4351-8a6a-d43a1bfb9eaa",
|
||||
"id": "41a0a8be-5004-4e6d-bdc5-9c7edf04eb51",
|
||||
"name": "Format Error",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
@@ -224,7 +245,7 @@
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "dec6cbc4-7550-40d3-9195-c4cc4f787b9b",
|
||||
"id": "da462967-0548-4d57-a6de-cb783c96ac07",
|
||||
"name": "Format Risk",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
@@ -241,7 +262,7 @@
|
||||
"appendAttribution": false
|
||||
}
|
||||
},
|
||||
"id": "6267b604-d39b-4cb7-98a5-2342cdced33b",
|
||||
"id": "254280fd-f547-4302-97a5-30b44d851e12",
|
||||
"name": "Telegram Success",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1.1,
|
||||
@@ -259,12 +280,12 @@
|
||||
{
|
||||
"parameters": {
|
||||
"chatId": "579304651",
|
||||
"text": "{{ `🟢 TRADE OPENED\\n\\n📊 Symbol: ${$('Parse Signal').item.json.symbol}\\n${$('Parse Signal').item.json.direction === 'long' ? '📈' : '📉'} Direction: ${$('Parse Signal').item.json.direction.toUpperCase()}\\n\\n💰 Entry: $${$json.entryPrice.toFixed(4)}\\n🎯 TP1: $${$json.takeProfit1.toFixed(4)} (${$json.tp1Percent}%)\\n🎯 TP2: $${$json.takeProfit2.toFixed(4)} (${$json.tp2Percent}%)\\n🛑 SL: $${$json.stopLoss.toFixed(4)} (${$json.stopLossPercent}%)\\n\\n⏰ ${$now.toFormat('HH:mm:ss')}\\n✅ Position monitored` }}",
|
||||
"text": "={{ $json.message }}",
|
||||
"additionalFields": {
|
||||
"appendAttribution": false
|
||||
}
|
||||
},
|
||||
"id": "88224fac-ef7a-41ec-b68a-e4bc1a5e3f31",
|
||||
"id": "4ea066c9-4971-408f-b6e2-7d704c13ef55",
|
||||
"name": "Telegram Error",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1.1,
|
||||
@@ -287,7 +308,7 @@
|
||||
"appendAttribution": false
|
||||
}
|
||||
},
|
||||
"id": "4eccaca4-a5e7-407f-aab9-663a98a8323b",
|
||||
"id": "ee6be7be-1735-4fa3-bd33-6b3fde9414d3",
|
||||
"name": "Telegram Risk",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1.1,
|
||||
@@ -304,46 +325,23 @@
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"chatId": "579304651",
|
||||
"text": "={{ $json.signal.startsWith(\"Buy\") ? \"🟢 \" + $json.signal : \"🔴 \" + $json.signal }}\n",
|
||||
"additionalFields": {
|
||||
"appendAttribution": false
|
||||
}
|
||||
},
|
||||
"id": "5a8eda4d-8945-4144-8672-022c9ee68bf6",
|
||||
"name": "Telegram",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1.1,
|
||||
"position": [
|
||||
-340,
|
||||
840
|
||||
],
|
||||
"credentials": {
|
||||
"telegramApi": {
|
||||
"id": "Csk5cg4HtaSqP5jJ",
|
||||
"name": "Telegram account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fields": {
|
||||
"values": [
|
||||
"conditions": {
|
||||
"string": [
|
||||
{
|
||||
"name": "signal",
|
||||
"stringValue": "={{ $json.body.split('|')[0].trim() }}"
|
||||
"value1": "={{ $json.timeframe }}",
|
||||
"operation": "equals",
|
||||
"value2": "5"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
},
|
||||
"id": "cce16424-fbb1-4191-b719-79ccfd59ec12",
|
||||
"name": "Edit Fields",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"id": "8c680565-120d-47dc-83b2-58dcd397168b",
|
||||
"name": "5min Chart Only?1",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-660,
|
||||
840
|
||||
-560,
|
||||
800
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -364,12 +362,12 @@
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Check Risk",
|
||||
"node": "15min Chart Only?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Telegram",
|
||||
"node": "5min Chart Only?1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
@@ -467,18 +465,35 @@
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields": {
|
||||
"15min Chart Only?": {
|
||||
"main": [
|
||||
[]
|
||||
[
|
||||
{
|
||||
"node": "Check Risk",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"5min Chart Only?1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Check Risk",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"active": true,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "2cc10693-953a-4b97-8c86-750b3063096b",
|
||||
"id": "xTCaxlyI02bQLxun",
|
||||
"versionId": "1376fb3b-08fb-4d96-a038-371249d36eda",
|
||||
"id": "gUDqTiHyHSfRUXv6",
|
||||
"meta": {
|
||||
"instanceId": "e766d4f0b5def8ee8cb8561cd9d2b9ba7733e1907990b6987bca40175f82c379"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user