@tradejs/node 2.0.2 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backtest.js +118 -5
- package/dist/backtest.mjs +118 -5
- package/dist/cli.js +12 -2
- package/dist/cli.mjs +12 -2
- package/dist/strategies.d.mts +2 -1
- package/dist/strategies.d.ts +2 -1
- package/dist/strategies.js +117 -16
- package/dist/strategies.mjs +117 -16
- package/package.json +4 -4
package/dist/backtest.js
CHANGED
|
@@ -2895,7 +2895,10 @@ var createTestConnector = (connector, context) => {
|
|
|
2895
2895
|
}
|
|
2896
2896
|
},
|
|
2897
2897
|
placeOrder: async (order) => {
|
|
2898
|
-
|
|
2898
|
+
const isPositionIncrease = Boolean(
|
|
2899
|
+
currentPosition && order.positionIntent === "increase" && currentPosition.direction === order.direction
|
|
2900
|
+
);
|
|
2901
|
+
if (currentPosition && !isPositionIncrease) {
|
|
2899
2902
|
return false;
|
|
2900
2903
|
}
|
|
2901
2904
|
const isLong = order.direction === "LONG";
|
|
@@ -2909,9 +2912,114 @@ var createTestConnector = (connector, context) => {
|
|
|
2909
2912
|
stage: "entry",
|
|
2910
2913
|
signal: order.signal
|
|
2911
2914
|
});
|
|
2912
|
-
|
|
2915
|
+
const previousPosition = currentPosition;
|
|
2916
|
+
const previousQty = previousPosition?.qty ?? 0;
|
|
2917
|
+
const resultingQty = previousQty + order.qty;
|
|
2918
|
+
const resultingEntryPrice = previousPosition ? getWeightedAverage(
|
|
2919
|
+
previousPosition.price,
|
|
2920
|
+
previousQty,
|
|
2921
|
+
entryPrice,
|
|
2922
|
+
order.qty
|
|
2923
|
+
) : entryPrice;
|
|
2924
|
+
currentPosition = previousPosition ? {
|
|
2925
|
+
...previousPosition,
|
|
2926
|
+
qty: resultingQty,
|
|
2927
|
+
price: resultingEntryPrice
|
|
2928
|
+
} : { ...order, price: entryPrice, amount };
|
|
2929
|
+
originalQty = resultingQty;
|
|
2930
|
+
if (isPositionIncrease) {
|
|
2931
|
+
const { fee: fee2, profit: profit2 } = getNetProfit({
|
|
2932
|
+
grossProfit: 0,
|
|
2933
|
+
price: entryPrice,
|
|
2934
|
+
qty: order.qty,
|
|
2935
|
+
feeRate: order.isLimit ? makerFeeRate : takerFeeRate
|
|
2936
|
+
});
|
|
2937
|
+
const entrySlippageCost2 = getSlippageCost({
|
|
2938
|
+
requestedPrice: order.price,
|
|
2939
|
+
executionPrice: entryPrice,
|
|
2940
|
+
direction: order.direction,
|
|
2941
|
+
stage: "entry",
|
|
2942
|
+
qty: order.qty
|
|
2943
|
+
});
|
|
2944
|
+
amount += profit2;
|
|
2945
|
+
currentPositionProfit += profit2;
|
|
2946
|
+
if (currentTradeResult) {
|
|
2947
|
+
const requestedEntryPrice = getWeightedAverage(
|
|
2948
|
+
currentTradeResult.requestedEntryPrice,
|
|
2949
|
+
currentTradeResult.qty,
|
|
2950
|
+
order.price,
|
|
2951
|
+
order.qty
|
|
2952
|
+
);
|
|
2953
|
+
const weightedEntryPrice = getWeightedAverage(
|
|
2954
|
+
currentTradeResult.entryPrice,
|
|
2955
|
+
currentTradeResult.qty,
|
|
2956
|
+
entryPrice,
|
|
2957
|
+
order.qty
|
|
2958
|
+
);
|
|
2959
|
+
const entryBaseSlippageBps = getWeightedAverage(
|
|
2960
|
+
currentTradeResult.entryBaseSlippageBps,
|
|
2961
|
+
currentTradeResult.qty,
|
|
2962
|
+
entrySlippageBreakdown.baseSlippageBps,
|
|
2963
|
+
order.qty
|
|
2964
|
+
);
|
|
2965
|
+
const entrySpreadBps = getWeightedAverage(
|
|
2966
|
+
currentTradeResult.entrySpreadBps,
|
|
2967
|
+
currentTradeResult.qty,
|
|
2968
|
+
entrySlippageBreakdown.spreadBps,
|
|
2969
|
+
order.qty
|
|
2970
|
+
);
|
|
2971
|
+
const entrySpreadSlippageBps = getWeightedAverage(
|
|
2972
|
+
currentTradeResult.entrySpreadSlippageBps,
|
|
2973
|
+
currentTradeResult.qty,
|
|
2974
|
+
entrySlippageBreakdown.spreadSlippageBps,
|
|
2975
|
+
order.qty
|
|
2976
|
+
);
|
|
2977
|
+
const entryMarketImpactBps = getWeightedAverage(
|
|
2978
|
+
currentTradeResult.entryMarketImpactBps,
|
|
2979
|
+
currentTradeResult.qty,
|
|
2980
|
+
entrySlippageBreakdown.marketImpactBps,
|
|
2981
|
+
order.qty
|
|
2982
|
+
);
|
|
2983
|
+
const entryDelayRiskBps = getWeightedAverage(
|
|
2984
|
+
currentTradeResult.entryDelayRiskBps,
|
|
2985
|
+
currentTradeResult.qty,
|
|
2986
|
+
entrySlippageBreakdown.delayRiskBps,
|
|
2987
|
+
order.qty
|
|
2988
|
+
);
|
|
2989
|
+
const totalEntrySlippageCost = currentTradeResult.entrySlippageCost + entrySlippageCost2;
|
|
2990
|
+
currentTradeResult = {
|
|
2991
|
+
...currentTradeResult,
|
|
2992
|
+
qty: currentTradeResult.qty + order.qty,
|
|
2993
|
+
requestedEntryPrice,
|
|
2994
|
+
entryPrice: weightedEntryPrice,
|
|
2995
|
+
netProfit: currentTradeResult.netProfit + profit2,
|
|
2996
|
+
openFee: currentTradeResult.openFee + fee2,
|
|
2997
|
+
totalFee: currentTradeResult.totalFee + fee2,
|
|
2998
|
+
entrySlippagePrice: weightedEntryPrice - requestedEntryPrice,
|
|
2999
|
+
entrySlippageBps: getSlippageBps(
|
|
3000
|
+
requestedEntryPrice,
|
|
3001
|
+
weightedEntryPrice
|
|
3002
|
+
),
|
|
3003
|
+
entryBaseSlippageBps,
|
|
3004
|
+
entrySpreadBps,
|
|
3005
|
+
entrySpreadSlippageBps,
|
|
3006
|
+
entryMarketImpactBps,
|
|
3007
|
+
entryDelayRiskBps,
|
|
3008
|
+
entrySlippageCost: totalEntrySlippageCost,
|
|
3009
|
+
totalSlippageCost: totalEntrySlippageCost + currentTradeResult.exitSlippageCost
|
|
3010
|
+
};
|
|
3011
|
+
}
|
|
3012
|
+
logOrder({
|
|
3013
|
+
...order,
|
|
3014
|
+
price: entryPrice,
|
|
3015
|
+
profit: profit2,
|
|
3016
|
+
fee: fee2,
|
|
3017
|
+
type: isLong ? "OPEN_LONG" : "OPEN_SHORT",
|
|
3018
|
+
...getExecutionSlippageLogData(entrySlippageBreakdown, "entry")
|
|
3019
|
+
});
|
|
3020
|
+
return true;
|
|
3021
|
+
}
|
|
2913
3022
|
currentSignalId = typeof order.signal?.signalId === "string" && order.signal.signalId ? order.signal.signalId : null;
|
|
2914
|
-
originalQty = order.qty;
|
|
2915
3023
|
const { fee, profit } = getNetProfit({
|
|
2916
3024
|
grossProfit: 0,
|
|
2917
3025
|
price: entryPrice,
|
|
@@ -2978,16 +3086,21 @@ var createTestConnector = (connector, context) => {
|
|
|
2978
3086
|
return false;
|
|
2979
3087
|
}
|
|
2980
3088
|
takeProfits = Array.isArray(nextTakeProfits) ? nextTakeProfits.map((tp) => ({ ...tp })) : [];
|
|
3089
|
+
const fullTakeProfit = takeProfits.length === 1 && takeProfits[0]?.rate === 1 ? takeProfits[0].price : void 0;
|
|
3090
|
+
currentPosition = {
|
|
3091
|
+
...currentPosition,
|
|
3092
|
+
tpPrice: fullTakeProfit
|
|
3093
|
+
};
|
|
2981
3094
|
return true;
|
|
2982
3095
|
},
|
|
2983
3096
|
setStopLoss: async ({ stopLossPrice: nextStopLossPrice }) => {
|
|
2984
3097
|
if (!currentPosition) {
|
|
2985
3098
|
return false;
|
|
2986
3099
|
}
|
|
2987
|
-
stopLossPrice = nextStopLossPrice
|
|
3100
|
+
stopLossPrice = nextStopLossPrice ?? null;
|
|
2988
3101
|
currentPosition = {
|
|
2989
3102
|
...currentPosition,
|
|
2990
|
-
|
|
3103
|
+
slPrice: stopLossPrice ?? void 0
|
|
2991
3104
|
};
|
|
2992
3105
|
return true;
|
|
2993
3106
|
},
|
package/dist/backtest.mjs
CHANGED
|
@@ -625,7 +625,10 @@ var createTestConnector = (connector, context) => {
|
|
|
625
625
|
}
|
|
626
626
|
},
|
|
627
627
|
placeOrder: async (order) => {
|
|
628
|
-
|
|
628
|
+
const isPositionIncrease = Boolean(
|
|
629
|
+
currentPosition && order.positionIntent === "increase" && currentPosition.direction === order.direction
|
|
630
|
+
);
|
|
631
|
+
if (currentPosition && !isPositionIncrease) {
|
|
629
632
|
return false;
|
|
630
633
|
}
|
|
631
634
|
const isLong = order.direction === "LONG";
|
|
@@ -639,9 +642,114 @@ var createTestConnector = (connector, context) => {
|
|
|
639
642
|
stage: "entry",
|
|
640
643
|
signal: order.signal
|
|
641
644
|
});
|
|
642
|
-
|
|
645
|
+
const previousPosition = currentPosition;
|
|
646
|
+
const previousQty = previousPosition?.qty ?? 0;
|
|
647
|
+
const resultingQty = previousQty + order.qty;
|
|
648
|
+
const resultingEntryPrice = previousPosition ? getWeightedAverage(
|
|
649
|
+
previousPosition.price,
|
|
650
|
+
previousQty,
|
|
651
|
+
entryPrice,
|
|
652
|
+
order.qty
|
|
653
|
+
) : entryPrice;
|
|
654
|
+
currentPosition = previousPosition ? {
|
|
655
|
+
...previousPosition,
|
|
656
|
+
qty: resultingQty,
|
|
657
|
+
price: resultingEntryPrice
|
|
658
|
+
} : { ...order, price: entryPrice, amount };
|
|
659
|
+
originalQty = resultingQty;
|
|
660
|
+
if (isPositionIncrease) {
|
|
661
|
+
const { fee: fee2, profit: profit2 } = getNetProfit({
|
|
662
|
+
grossProfit: 0,
|
|
663
|
+
price: entryPrice,
|
|
664
|
+
qty: order.qty,
|
|
665
|
+
feeRate: order.isLimit ? makerFeeRate : takerFeeRate
|
|
666
|
+
});
|
|
667
|
+
const entrySlippageCost2 = getSlippageCost({
|
|
668
|
+
requestedPrice: order.price,
|
|
669
|
+
executionPrice: entryPrice,
|
|
670
|
+
direction: order.direction,
|
|
671
|
+
stage: "entry",
|
|
672
|
+
qty: order.qty
|
|
673
|
+
});
|
|
674
|
+
amount += profit2;
|
|
675
|
+
currentPositionProfit += profit2;
|
|
676
|
+
if (currentTradeResult) {
|
|
677
|
+
const requestedEntryPrice = getWeightedAverage(
|
|
678
|
+
currentTradeResult.requestedEntryPrice,
|
|
679
|
+
currentTradeResult.qty,
|
|
680
|
+
order.price,
|
|
681
|
+
order.qty
|
|
682
|
+
);
|
|
683
|
+
const weightedEntryPrice = getWeightedAverage(
|
|
684
|
+
currentTradeResult.entryPrice,
|
|
685
|
+
currentTradeResult.qty,
|
|
686
|
+
entryPrice,
|
|
687
|
+
order.qty
|
|
688
|
+
);
|
|
689
|
+
const entryBaseSlippageBps = getWeightedAverage(
|
|
690
|
+
currentTradeResult.entryBaseSlippageBps,
|
|
691
|
+
currentTradeResult.qty,
|
|
692
|
+
entrySlippageBreakdown.baseSlippageBps,
|
|
693
|
+
order.qty
|
|
694
|
+
);
|
|
695
|
+
const entrySpreadBps = getWeightedAverage(
|
|
696
|
+
currentTradeResult.entrySpreadBps,
|
|
697
|
+
currentTradeResult.qty,
|
|
698
|
+
entrySlippageBreakdown.spreadBps,
|
|
699
|
+
order.qty
|
|
700
|
+
);
|
|
701
|
+
const entrySpreadSlippageBps = getWeightedAverage(
|
|
702
|
+
currentTradeResult.entrySpreadSlippageBps,
|
|
703
|
+
currentTradeResult.qty,
|
|
704
|
+
entrySlippageBreakdown.spreadSlippageBps,
|
|
705
|
+
order.qty
|
|
706
|
+
);
|
|
707
|
+
const entryMarketImpactBps = getWeightedAverage(
|
|
708
|
+
currentTradeResult.entryMarketImpactBps,
|
|
709
|
+
currentTradeResult.qty,
|
|
710
|
+
entrySlippageBreakdown.marketImpactBps,
|
|
711
|
+
order.qty
|
|
712
|
+
);
|
|
713
|
+
const entryDelayRiskBps = getWeightedAverage(
|
|
714
|
+
currentTradeResult.entryDelayRiskBps,
|
|
715
|
+
currentTradeResult.qty,
|
|
716
|
+
entrySlippageBreakdown.delayRiskBps,
|
|
717
|
+
order.qty
|
|
718
|
+
);
|
|
719
|
+
const totalEntrySlippageCost = currentTradeResult.entrySlippageCost + entrySlippageCost2;
|
|
720
|
+
currentTradeResult = {
|
|
721
|
+
...currentTradeResult,
|
|
722
|
+
qty: currentTradeResult.qty + order.qty,
|
|
723
|
+
requestedEntryPrice,
|
|
724
|
+
entryPrice: weightedEntryPrice,
|
|
725
|
+
netProfit: currentTradeResult.netProfit + profit2,
|
|
726
|
+
openFee: currentTradeResult.openFee + fee2,
|
|
727
|
+
totalFee: currentTradeResult.totalFee + fee2,
|
|
728
|
+
entrySlippagePrice: weightedEntryPrice - requestedEntryPrice,
|
|
729
|
+
entrySlippageBps: getSlippageBps(
|
|
730
|
+
requestedEntryPrice,
|
|
731
|
+
weightedEntryPrice
|
|
732
|
+
),
|
|
733
|
+
entryBaseSlippageBps,
|
|
734
|
+
entrySpreadBps,
|
|
735
|
+
entrySpreadSlippageBps,
|
|
736
|
+
entryMarketImpactBps,
|
|
737
|
+
entryDelayRiskBps,
|
|
738
|
+
entrySlippageCost: totalEntrySlippageCost,
|
|
739
|
+
totalSlippageCost: totalEntrySlippageCost + currentTradeResult.exitSlippageCost
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
logOrder({
|
|
743
|
+
...order,
|
|
744
|
+
price: entryPrice,
|
|
745
|
+
profit: profit2,
|
|
746
|
+
fee: fee2,
|
|
747
|
+
type: isLong ? "OPEN_LONG" : "OPEN_SHORT",
|
|
748
|
+
...getExecutionSlippageLogData(entrySlippageBreakdown, "entry")
|
|
749
|
+
});
|
|
750
|
+
return true;
|
|
751
|
+
}
|
|
643
752
|
currentSignalId = typeof order.signal?.signalId === "string" && order.signal.signalId ? order.signal.signalId : null;
|
|
644
|
-
originalQty = order.qty;
|
|
645
753
|
const { fee, profit } = getNetProfit({
|
|
646
754
|
grossProfit: 0,
|
|
647
755
|
price: entryPrice,
|
|
@@ -708,16 +816,21 @@ var createTestConnector = (connector, context) => {
|
|
|
708
816
|
return false;
|
|
709
817
|
}
|
|
710
818
|
takeProfits = Array.isArray(nextTakeProfits) ? nextTakeProfits.map((tp) => ({ ...tp })) : [];
|
|
819
|
+
const fullTakeProfit = takeProfits.length === 1 && takeProfits[0]?.rate === 1 ? takeProfits[0].price : void 0;
|
|
820
|
+
currentPosition = {
|
|
821
|
+
...currentPosition,
|
|
822
|
+
tpPrice: fullTakeProfit
|
|
823
|
+
};
|
|
711
824
|
return true;
|
|
712
825
|
},
|
|
713
826
|
setStopLoss: async ({ stopLossPrice: nextStopLossPrice }) => {
|
|
714
827
|
if (!currentPosition) {
|
|
715
828
|
return false;
|
|
716
829
|
}
|
|
717
|
-
stopLossPrice = nextStopLossPrice
|
|
830
|
+
stopLossPrice = nextStopLossPrice ?? null;
|
|
718
831
|
currentPosition = {
|
|
719
832
|
...currentPosition,
|
|
720
|
-
|
|
833
|
+
slPrice: stopLossPrice ?? void 0
|
|
721
834
|
};
|
|
722
835
|
return true;
|
|
723
836
|
},
|
package/dist/cli.js
CHANGED
|
@@ -7071,7 +7071,8 @@ var SCREENSHOT_NAVIGATION_RETRY_DELAY_MS = 2e3;
|
|
|
7071
7071
|
var SCREENSHOT_CAPTURE_ATTEMPTS = 2;
|
|
7072
7072
|
var SCREENSHOT_CAPTURE_RETRY_DELAY_MS = 2e3;
|
|
7073
7073
|
var SCREENSHOT_NAVIGATION_TIMEOUT_MS = 3e4;
|
|
7074
|
-
var
|
|
7074
|
+
var SCREENSHOT_READY_TIMEOUT_MS = 3e4;
|
|
7075
|
+
var SCREENSHOT_READY_SELECTOR = '[data-screenshot-ready="true"]';
|
|
7075
7076
|
var SCREENSHOT_CONSOLE_LOG_LIMIT = 20;
|
|
7076
7077
|
var SCREENSHOT_CONSOLE_TEXT_LIMIT = 500;
|
|
7077
7078
|
var SCREENSHOT_BROWSER_STDERR = process.env.SCREENSHOT_BROWSER_STDERR === "1";
|
|
@@ -7400,7 +7401,16 @@ var screenDashboard = async (signal, projectRoot, userName = "root") => {
|
|
|
7400
7401
|
`Failed to open dashboard ${dashboardUrl}: ${gotoError.message || String(gotoError)}`
|
|
7401
7402
|
);
|
|
7402
7403
|
}
|
|
7403
|
-
await (
|
|
7404
|
+
await page.waitForSelector(SCREENSHOT_READY_SELECTOR, {
|
|
7405
|
+
timeout: SCREENSHOT_READY_TIMEOUT_MS
|
|
7406
|
+
});
|
|
7407
|
+
await page.evaluate(
|
|
7408
|
+
() => new Promise((resolve) => {
|
|
7409
|
+
requestAnimationFrame(
|
|
7410
|
+
() => requestAnimationFrame(() => resolve())
|
|
7411
|
+
);
|
|
7412
|
+
})
|
|
7413
|
+
);
|
|
7404
7414
|
await import_promises.default.mkdir(getScreenshotsDir(projectRoot), { recursive: true });
|
|
7405
7415
|
await page.screenshot({
|
|
7406
7416
|
path: screenshotPath,
|
package/dist/cli.mjs
CHANGED
|
@@ -74,7 +74,8 @@ var SCREENSHOT_NAVIGATION_RETRY_DELAY_MS = 2e3;
|
|
|
74
74
|
var SCREENSHOT_CAPTURE_ATTEMPTS = 2;
|
|
75
75
|
var SCREENSHOT_CAPTURE_RETRY_DELAY_MS = 2e3;
|
|
76
76
|
var SCREENSHOT_NAVIGATION_TIMEOUT_MS = 3e4;
|
|
77
|
-
var
|
|
77
|
+
var SCREENSHOT_READY_TIMEOUT_MS = 3e4;
|
|
78
|
+
var SCREENSHOT_READY_SELECTOR = '[data-screenshot-ready="true"]';
|
|
78
79
|
var SCREENSHOT_CONSOLE_LOG_LIMIT = 20;
|
|
79
80
|
var SCREENSHOT_CONSOLE_TEXT_LIMIT = 500;
|
|
80
81
|
var SCREENSHOT_BROWSER_STDERR = process.env.SCREENSHOT_BROWSER_STDERR === "1";
|
|
@@ -403,7 +404,16 @@ var screenDashboard = async (signal, projectRoot, userName = "root") => {
|
|
|
403
404
|
`Failed to open dashboard ${dashboardUrl}: ${gotoError.message || String(gotoError)}`
|
|
404
405
|
);
|
|
405
406
|
}
|
|
406
|
-
await
|
|
407
|
+
await page.waitForSelector(SCREENSHOT_READY_SELECTOR, {
|
|
408
|
+
timeout: SCREENSHOT_READY_TIMEOUT_MS
|
|
409
|
+
});
|
|
410
|
+
await page.evaluate(
|
|
411
|
+
() => new Promise((resolve) => {
|
|
412
|
+
requestAnimationFrame(
|
|
413
|
+
() => requestAnimationFrame(() => resolve())
|
|
414
|
+
);
|
|
415
|
+
})
|
|
416
|
+
);
|
|
407
417
|
await fs.mkdir(getScreenshotsDir(projectRoot), { recursive: true });
|
|
408
418
|
await page.screenshot({
|
|
409
419
|
path: screenshotPath,
|
package/dist/strategies.d.mts
CHANGED
|
@@ -50,12 +50,13 @@ interface ExecuteEntryOrderParams {
|
|
|
50
50
|
timestamp: number;
|
|
51
51
|
takeProfits: Tp[];
|
|
52
52
|
stopLossPrice: number | null;
|
|
53
|
+
positionIntent?: 'open' | 'increase';
|
|
53
54
|
leverage?: number;
|
|
54
55
|
signal: Signal;
|
|
55
56
|
beforePlaceOrder?: () => Promise<void>;
|
|
56
57
|
recordRuntimeTrade?: boolean;
|
|
57
58
|
}
|
|
58
|
-
declare const executeEntryOrder: ({ connector, userName, symbol, direction, qty, currentPrice, timestamp, takeProfits, stopLossPrice, signal, beforePlaceOrder, recordRuntimeTrade, leverage, }: ExecuteEntryOrderParams) => Promise<number>;
|
|
59
|
+
declare const executeEntryOrder: ({ connector, userName, symbol, direction, qty, currentPrice, timestamp, takeProfits, stopLossPrice, positionIntent, signal, beforePlaceOrder, recordRuntimeTrade, leverage, }: ExecuteEntryOrderParams) => Promise<number>;
|
|
59
60
|
|
|
60
61
|
declare const enrichSignalWithBinanceMarketContext: (params: {
|
|
61
62
|
signal: Signal;
|
package/dist/strategies.d.ts
CHANGED
|
@@ -50,12 +50,13 @@ interface ExecuteEntryOrderParams {
|
|
|
50
50
|
timestamp: number;
|
|
51
51
|
takeProfits: Tp[];
|
|
52
52
|
stopLossPrice: number | null;
|
|
53
|
+
positionIntent?: 'open' | 'increase';
|
|
53
54
|
leverage?: number;
|
|
54
55
|
signal: Signal;
|
|
55
56
|
beforePlaceOrder?: () => Promise<void>;
|
|
56
57
|
recordRuntimeTrade?: boolean;
|
|
57
58
|
}
|
|
58
|
-
declare const executeEntryOrder: ({ connector, userName, symbol, direction, qty, currentPrice, timestamp, takeProfits, stopLossPrice, signal, beforePlaceOrder, recordRuntimeTrade, leverage, }: ExecuteEntryOrderParams) => Promise<number>;
|
|
59
|
+
declare const executeEntryOrder: ({ connector, userName, symbol, direction, qty, currentPrice, timestamp, takeProfits, stopLossPrice, positionIntent, signal, beforePlaceOrder, recordRuntimeTrade, leverage, }: ExecuteEntryOrderParams) => Promise<number>;
|
|
59
60
|
|
|
60
61
|
declare const enrichSignalWithBinanceMarketContext: (params: {
|
|
61
62
|
signal: Signal;
|
package/dist/strategies.js
CHANGED
|
@@ -7285,6 +7285,10 @@ var recordRuntimeTradeOpen = async (params) => {
|
|
|
7285
7285
|
}
|
|
7286
7286
|
const record = {
|
|
7287
7287
|
...params,
|
|
7288
|
+
entryCount: 1,
|
|
7289
|
+
lastEntryPrice: params.entryPrice,
|
|
7290
|
+
lastEntryQty: params.qty,
|
|
7291
|
+
lastEntryTimestamp: params.entryTimestamp,
|
|
7288
7292
|
status: "active",
|
|
7289
7293
|
currentPrice: params.entryPrice,
|
|
7290
7294
|
currentPnl: 0,
|
|
@@ -7321,6 +7325,73 @@ var recordRuntimeTradeOpen = async (params) => {
|
|
|
7321
7325
|
}
|
|
7322
7326
|
return record;
|
|
7323
7327
|
};
|
|
7328
|
+
var recordRuntimeTradeIncrease = async (params) => {
|
|
7329
|
+
const {
|
|
7330
|
+
userName,
|
|
7331
|
+
strategy,
|
|
7332
|
+
symbol,
|
|
7333
|
+
direction,
|
|
7334
|
+
resultingQty,
|
|
7335
|
+
resultingEntryPrice,
|
|
7336
|
+
addedQty,
|
|
7337
|
+
addedEntryPrice,
|
|
7338
|
+
entryTimestamp,
|
|
7339
|
+
fee,
|
|
7340
|
+
accountId,
|
|
7341
|
+
deploymentId
|
|
7342
|
+
} = params;
|
|
7343
|
+
if (!userName) {
|
|
7344
|
+
return null;
|
|
7345
|
+
}
|
|
7346
|
+
const existing = await getActiveRuntimeTrade({
|
|
7347
|
+
userName,
|
|
7348
|
+
symbol,
|
|
7349
|
+
accountId,
|
|
7350
|
+
deploymentId
|
|
7351
|
+
});
|
|
7352
|
+
if (!existing || existing.strategy !== strategy || existing.direction !== direction) {
|
|
7353
|
+
return null;
|
|
7354
|
+
}
|
|
7355
|
+
const addedFee = typeof fee === "number" && Number.isFinite(fee) ? fee : 0;
|
|
7356
|
+
const openFee = (existing.openFee ?? existing.fee ?? 0) + addedFee;
|
|
7357
|
+
const totalFee = (existing.totalFee ?? existing.fee ?? 0) + addedFee;
|
|
7358
|
+
const next = {
|
|
7359
|
+
...existing,
|
|
7360
|
+
qty: resultingQty,
|
|
7361
|
+
entryPrice: resultingEntryPrice,
|
|
7362
|
+
entryCount: Math.max(1, existing.entryCount ?? 1) + 1,
|
|
7363
|
+
lastEntryPrice: addedEntryPrice,
|
|
7364
|
+
lastEntryQty: addedQty,
|
|
7365
|
+
lastEntryTimestamp: entryTimestamp,
|
|
7366
|
+
currentPrice: resultingEntryPrice,
|
|
7367
|
+
currentPnl: 0,
|
|
7368
|
+
fee: openFee,
|
|
7369
|
+
openFee,
|
|
7370
|
+
totalFee,
|
|
7371
|
+
lastSyncedAt: now()
|
|
7372
|
+
};
|
|
7373
|
+
const dayKey = (0, import_time.getRuntimeStorageDayKey)(existing.entryTimestamp);
|
|
7374
|
+
try {
|
|
7375
|
+
await Promise.all([
|
|
7376
|
+
(0, import_redis2.setData)(import_redis2.redisKeys.runtimeTrade(userName, existing.orderId), next, {
|
|
7377
|
+
expire: 0
|
|
7378
|
+
}),
|
|
7379
|
+
(0, import_redis2.setHashJsonField)(
|
|
7380
|
+
import_redis2.redisKeys.runtimeTradeBucket(userName, dayKey),
|
|
7381
|
+
existing.orderId,
|
|
7382
|
+
next,
|
|
7383
|
+
{ expire: 0 }
|
|
7384
|
+
)
|
|
7385
|
+
]);
|
|
7386
|
+
} catch (error) {
|
|
7387
|
+
import_logger3.logger.error(
|
|
7388
|
+
"runtime trade increase journal failed: %s %s",
|
|
7389
|
+
symbol,
|
|
7390
|
+
error?.message || String(error)
|
|
7391
|
+
);
|
|
7392
|
+
}
|
|
7393
|
+
return next;
|
|
7394
|
+
};
|
|
7324
7395
|
var getActiveRuntimeTrade = async (params) => {
|
|
7325
7396
|
const { userName, symbol, accountId, deploymentId } = params;
|
|
7326
7397
|
if (!userName) {
|
|
@@ -8633,12 +8704,14 @@ var executeEntryOrder = async ({
|
|
|
8633
8704
|
timestamp,
|
|
8634
8705
|
takeProfits,
|
|
8635
8706
|
stopLossPrice,
|
|
8707
|
+
positionIntent = "open",
|
|
8636
8708
|
signal,
|
|
8637
8709
|
beforePlaceOrder,
|
|
8638
8710
|
recordRuntimeTrade = true,
|
|
8639
8711
|
leverage
|
|
8640
8712
|
}) => {
|
|
8641
8713
|
await beforePlaceOrder?.();
|
|
8714
|
+
const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
|
|
8642
8715
|
const orderId = signal.orderId || createRuntimeOrderId(signal.strategy);
|
|
8643
8716
|
const signalTimestamp = signal.timestamp;
|
|
8644
8717
|
const signalClosePrice = currentPrice;
|
|
@@ -8653,6 +8726,7 @@ var executeEntryOrder = async ({
|
|
|
8653
8726
|
qty,
|
|
8654
8727
|
price: currentPrice,
|
|
8655
8728
|
isLimit: false,
|
|
8729
|
+
positionIntent,
|
|
8656
8730
|
timestamp,
|
|
8657
8731
|
direction,
|
|
8658
8732
|
...typeof leverage === "number" && Number.isFinite(leverage) ? { leverage } : {},
|
|
@@ -8663,27 +8737,36 @@ var executeEntryOrder = async ({
|
|
|
8663
8737
|
const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
|
|
8664
8738
|
const currentPosition = await connector.getPosition(symbol);
|
|
8665
8739
|
const fillTime = Date.now();
|
|
8666
|
-
const
|
|
8667
|
-
const
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8740
|
+
const isPositionIncrease = positionIntent === "increase" && previousPosition?.direction === direction && Number.isFinite(previousPosition.qty) && previousPosition.qty > 0;
|
|
8741
|
+
const hasRefreshedIncreasedPosition = Boolean(
|
|
8742
|
+
isPositionIncrease && previousPosition && currentPosition?.qty && currentPosition.qty > previousPosition.qty
|
|
8743
|
+
);
|
|
8744
|
+
const hasUsableCurrentPosition = Boolean(
|
|
8745
|
+
currentPosition && Number.isFinite(currentPosition.price) && Number.isFinite(currentPosition.qty) && (!isPositionIncrease || hasRefreshedIncreasedPosition)
|
|
8746
|
+
);
|
|
8747
|
+
const resultingEntryPrice = hasUsableCurrentPosition && currentPosition ? currentPosition.price : isPositionIncrease && previousPosition ? (previousPosition.price * previousPosition.qty + currentPrice * placedQty) / (previousPosition.qty + placedQty) : currentPrice;
|
|
8748
|
+
const resultingQty = hasUsableCurrentPosition && currentPosition ? currentPosition.qty : isPositionIncrease && previousPosition ? previousPosition.qty + placedQty : placedQty;
|
|
8749
|
+
const filledQty = isPositionIncrease && previousPosition ? hasRefreshedIncreasedPosition && currentPosition ? currentPosition.qty - previousPosition.qty : placedQty : resultingQty;
|
|
8750
|
+
const fillPrice = isPositionIncrease && previousPosition && hasRefreshedIncreasedPosition && currentPosition ? (resultingEntryPrice * currentPosition.qty - previousPosition.price * previousPosition.qty) / (currentPosition.qty - previousPosition.qty) : isPositionIncrease ? currentPrice : resultingEntryPrice;
|
|
8751
|
+
const fillSource = hasUsableCurrentPosition ? "exchange_position" : orderPlaced ? "requested_price" : "unknown";
|
|
8752
|
+
const estimatedOpenFee = fillPrice * filledQty * import_constants3.FEE_PERCENT;
|
|
8753
|
+
signal.prices.currentPrice = fillPrice;
|
|
8754
|
+
signal.orderQty = filledQty;
|
|
8755
|
+
signal.orderValue = filledQty * fillPrice;
|
|
8673
8756
|
if (orderPlaced) {
|
|
8674
8757
|
try {
|
|
8675
8758
|
await applyProtectiveOrders({
|
|
8676
8759
|
connector,
|
|
8677
8760
|
symbol,
|
|
8678
8761
|
direction,
|
|
8679
|
-
qty:
|
|
8762
|
+
qty: resultingQty,
|
|
8680
8763
|
takeProfits,
|
|
8681
8764
|
stopLossPrice
|
|
8682
8765
|
});
|
|
8683
8766
|
} catch (error) {
|
|
8684
8767
|
await connector.closePosition({
|
|
8685
8768
|
symbol,
|
|
8686
|
-
price:
|
|
8769
|
+
price: resultingEntryPrice,
|
|
8687
8770
|
timestamp,
|
|
8688
8771
|
direction,
|
|
8689
8772
|
signal
|
|
@@ -8696,7 +8779,7 @@ var executeEntryOrder = async ({
|
|
|
8696
8779
|
if (orderPlaced) {
|
|
8697
8780
|
signal.orderFailureReason = void 0;
|
|
8698
8781
|
}
|
|
8699
|
-
if (orderPlaced && recordRuntimeTrade) {
|
|
8782
|
+
if (orderPlaced && recordRuntimeTrade && !isPositionIncrease) {
|
|
8700
8783
|
await recordRuntimeTradeOpen({
|
|
8701
8784
|
userName,
|
|
8702
8785
|
orderId,
|
|
@@ -8705,8 +8788,8 @@ var executeEntryOrder = async ({
|
|
|
8705
8788
|
symbol,
|
|
8706
8789
|
interval: signal.interval,
|
|
8707
8790
|
direction,
|
|
8708
|
-
qty:
|
|
8709
|
-
entryPrice,
|
|
8791
|
+
qty: resultingQty,
|
|
8792
|
+
entryPrice: resultingEntryPrice,
|
|
8710
8793
|
signalTimestamp,
|
|
8711
8794
|
signalClosePrice,
|
|
8712
8795
|
arrivalSnapshotTime: arrivalSnapshot.arrivalSnapshotTime,
|
|
@@ -8717,7 +8800,7 @@ var executeEntryOrder = async ({
|
|
|
8717
8800
|
spreadBps: arrivalSnapshot.spreadBps,
|
|
8718
8801
|
orderSubmitTime,
|
|
8719
8802
|
orderAckTime,
|
|
8720
|
-
fillAvgPrice:
|
|
8803
|
+
fillAvgPrice: fillPrice,
|
|
8721
8804
|
fillSource,
|
|
8722
8805
|
fillTime,
|
|
8723
8806
|
telemetryQuality: resolveRuntimeTelemetryQuality({
|
|
@@ -8725,7 +8808,7 @@ var executeEntryOrder = async ({
|
|
|
8725
8808
|
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
8726
8809
|
orderSubmitTime,
|
|
8727
8810
|
orderAckTime,
|
|
8728
|
-
fillAvgPrice:
|
|
8811
|
+
fillAvgPrice: fillPrice,
|
|
8729
8812
|
fillTime
|
|
8730
8813
|
}),
|
|
8731
8814
|
fee: estimatedOpenFee,
|
|
@@ -8741,10 +8824,26 @@ var executeEntryOrder = async ({
|
|
|
8741
8824
|
...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {}
|
|
8742
8825
|
});
|
|
8743
8826
|
}
|
|
8744
|
-
if (
|
|
8827
|
+
if (orderPlaced && recordRuntimeTrade && isPositionIncrease) {
|
|
8828
|
+
await recordRuntimeTradeIncrease({
|
|
8829
|
+
userName,
|
|
8830
|
+
strategy: signal.strategy,
|
|
8831
|
+
symbol,
|
|
8832
|
+
direction,
|
|
8833
|
+
resultingQty,
|
|
8834
|
+
resultingEntryPrice,
|
|
8835
|
+
addedQty: filledQty,
|
|
8836
|
+
addedEntryPrice: fillPrice,
|
|
8837
|
+
entryTimestamp: timestamp,
|
|
8838
|
+
fee: estimatedOpenFee,
|
|
8839
|
+
accountId: signal.accountId,
|
|
8840
|
+
deploymentId: signal.deploymentId
|
|
8841
|
+
});
|
|
8842
|
+
}
|
|
8843
|
+
if (hasUsableCurrentPosition && currentPosition?.price) {
|
|
8745
8844
|
return currentPosition.price;
|
|
8746
8845
|
}
|
|
8747
|
-
return
|
|
8846
|
+
return resultingEntryPrice;
|
|
8748
8847
|
};
|
|
8749
8848
|
var updatePositionProtection = async ({
|
|
8750
8849
|
connector,
|
|
@@ -9517,6 +9616,7 @@ var executeEntryDecision = async ({
|
|
|
9517
9616
|
timestamp: decision.entryContext.timestamp,
|
|
9518
9617
|
takeProfits: decision.orderPlan.takeProfits,
|
|
9519
9618
|
stopLossPrice: decision.orderPlan.stopLossPrice,
|
|
9619
|
+
positionIntent: decision.orderPlan.positionIntent,
|
|
9520
9620
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {},
|
|
9521
9621
|
signal,
|
|
9522
9622
|
beforePlaceOrder,
|
|
@@ -9548,6 +9648,7 @@ var executeEntryDecision = async ({
|
|
|
9548
9648
|
price: decision.entryContext.prices.currentPrice,
|
|
9549
9649
|
timestamp: decision.entryContext.timestamp,
|
|
9550
9650
|
direction: decision.entryContext.direction,
|
|
9651
|
+
positionIntent: decision.orderPlan.positionIntent,
|
|
9551
9652
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {}
|
|
9552
9653
|
});
|
|
9553
9654
|
if (!orderPlaced) {
|
package/dist/strategies.mjs
CHANGED
|
@@ -119,6 +119,10 @@ var recordRuntimeTradeOpen = async (params) => {
|
|
|
119
119
|
}
|
|
120
120
|
const record = {
|
|
121
121
|
...params,
|
|
122
|
+
entryCount: 1,
|
|
123
|
+
lastEntryPrice: params.entryPrice,
|
|
124
|
+
lastEntryQty: params.qty,
|
|
125
|
+
lastEntryTimestamp: params.entryTimestamp,
|
|
122
126
|
status: "active",
|
|
123
127
|
currentPrice: params.entryPrice,
|
|
124
128
|
currentPnl: 0,
|
|
@@ -155,6 +159,73 @@ var recordRuntimeTradeOpen = async (params) => {
|
|
|
155
159
|
}
|
|
156
160
|
return record;
|
|
157
161
|
};
|
|
162
|
+
var recordRuntimeTradeIncrease = async (params) => {
|
|
163
|
+
const {
|
|
164
|
+
userName,
|
|
165
|
+
strategy,
|
|
166
|
+
symbol,
|
|
167
|
+
direction,
|
|
168
|
+
resultingQty,
|
|
169
|
+
resultingEntryPrice,
|
|
170
|
+
addedQty,
|
|
171
|
+
addedEntryPrice,
|
|
172
|
+
entryTimestamp,
|
|
173
|
+
fee,
|
|
174
|
+
accountId,
|
|
175
|
+
deploymentId
|
|
176
|
+
} = params;
|
|
177
|
+
if (!userName) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
const existing = await getActiveRuntimeTrade({
|
|
181
|
+
userName,
|
|
182
|
+
symbol,
|
|
183
|
+
accountId,
|
|
184
|
+
deploymentId
|
|
185
|
+
});
|
|
186
|
+
if (!existing || existing.strategy !== strategy || existing.direction !== direction) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
const addedFee = typeof fee === "number" && Number.isFinite(fee) ? fee : 0;
|
|
190
|
+
const openFee = (existing.openFee ?? existing.fee ?? 0) + addedFee;
|
|
191
|
+
const totalFee = (existing.totalFee ?? existing.fee ?? 0) + addedFee;
|
|
192
|
+
const next = {
|
|
193
|
+
...existing,
|
|
194
|
+
qty: resultingQty,
|
|
195
|
+
entryPrice: resultingEntryPrice,
|
|
196
|
+
entryCount: Math.max(1, existing.entryCount ?? 1) + 1,
|
|
197
|
+
lastEntryPrice: addedEntryPrice,
|
|
198
|
+
lastEntryQty: addedQty,
|
|
199
|
+
lastEntryTimestamp: entryTimestamp,
|
|
200
|
+
currentPrice: resultingEntryPrice,
|
|
201
|
+
currentPnl: 0,
|
|
202
|
+
fee: openFee,
|
|
203
|
+
openFee,
|
|
204
|
+
totalFee,
|
|
205
|
+
lastSyncedAt: now()
|
|
206
|
+
};
|
|
207
|
+
const dayKey = getRuntimeStorageDayKey(existing.entryTimestamp);
|
|
208
|
+
try {
|
|
209
|
+
await Promise.all([
|
|
210
|
+
setData(redisKeys.runtimeTrade(userName, existing.orderId), next, {
|
|
211
|
+
expire: 0
|
|
212
|
+
}),
|
|
213
|
+
setHashJsonField(
|
|
214
|
+
redisKeys.runtimeTradeBucket(userName, dayKey),
|
|
215
|
+
existing.orderId,
|
|
216
|
+
next,
|
|
217
|
+
{ expire: 0 }
|
|
218
|
+
)
|
|
219
|
+
]);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
logger.error(
|
|
222
|
+
"runtime trade increase journal failed: %s %s",
|
|
223
|
+
symbol,
|
|
224
|
+
error?.message || String(error)
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return next;
|
|
228
|
+
};
|
|
158
229
|
var getActiveRuntimeTrade = async (params) => {
|
|
159
230
|
const { userName, symbol, accountId, deploymentId } = params;
|
|
160
231
|
if (!userName) {
|
|
@@ -496,12 +567,14 @@ var executeEntryOrder = async ({
|
|
|
496
567
|
timestamp,
|
|
497
568
|
takeProfits,
|
|
498
569
|
stopLossPrice,
|
|
570
|
+
positionIntent = "open",
|
|
499
571
|
signal,
|
|
500
572
|
beforePlaceOrder,
|
|
501
573
|
recordRuntimeTrade = true,
|
|
502
574
|
leverage
|
|
503
575
|
}) => {
|
|
504
576
|
await beforePlaceOrder?.();
|
|
577
|
+
const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
|
|
505
578
|
const orderId = signal.orderId || createRuntimeOrderId(signal.strategy);
|
|
506
579
|
const signalTimestamp = signal.timestamp;
|
|
507
580
|
const signalClosePrice = currentPrice;
|
|
@@ -516,6 +589,7 @@ var executeEntryOrder = async ({
|
|
|
516
589
|
qty,
|
|
517
590
|
price: currentPrice,
|
|
518
591
|
isLimit: false,
|
|
592
|
+
positionIntent,
|
|
519
593
|
timestamp,
|
|
520
594
|
direction,
|
|
521
595
|
...typeof leverage === "number" && Number.isFinite(leverage) ? { leverage } : {},
|
|
@@ -526,27 +600,36 @@ var executeEntryOrder = async ({
|
|
|
526
600
|
const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
|
|
527
601
|
const currentPosition = await connector.getPosition(symbol);
|
|
528
602
|
const fillTime = Date.now();
|
|
529
|
-
const
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
603
|
+
const isPositionIncrease = positionIntent === "increase" && previousPosition?.direction === direction && Number.isFinite(previousPosition.qty) && previousPosition.qty > 0;
|
|
604
|
+
const hasRefreshedIncreasedPosition = Boolean(
|
|
605
|
+
isPositionIncrease && previousPosition && currentPosition?.qty && currentPosition.qty > previousPosition.qty
|
|
606
|
+
);
|
|
607
|
+
const hasUsableCurrentPosition = Boolean(
|
|
608
|
+
currentPosition && Number.isFinite(currentPosition.price) && Number.isFinite(currentPosition.qty) && (!isPositionIncrease || hasRefreshedIncreasedPosition)
|
|
609
|
+
);
|
|
610
|
+
const resultingEntryPrice = hasUsableCurrentPosition && currentPosition ? currentPosition.price : isPositionIncrease && previousPosition ? (previousPosition.price * previousPosition.qty + currentPrice * placedQty) / (previousPosition.qty + placedQty) : currentPrice;
|
|
611
|
+
const resultingQty = hasUsableCurrentPosition && currentPosition ? currentPosition.qty : isPositionIncrease && previousPosition ? previousPosition.qty + placedQty : placedQty;
|
|
612
|
+
const filledQty = isPositionIncrease && previousPosition ? hasRefreshedIncreasedPosition && currentPosition ? currentPosition.qty - previousPosition.qty : placedQty : resultingQty;
|
|
613
|
+
const fillPrice = isPositionIncrease && previousPosition && hasRefreshedIncreasedPosition && currentPosition ? (resultingEntryPrice * currentPosition.qty - previousPosition.price * previousPosition.qty) / (currentPosition.qty - previousPosition.qty) : isPositionIncrease ? currentPrice : resultingEntryPrice;
|
|
614
|
+
const fillSource = hasUsableCurrentPosition ? "exchange_position" : orderPlaced ? "requested_price" : "unknown";
|
|
615
|
+
const estimatedOpenFee = fillPrice * filledQty * FEE_PERCENT;
|
|
616
|
+
signal.prices.currentPrice = fillPrice;
|
|
617
|
+
signal.orderQty = filledQty;
|
|
618
|
+
signal.orderValue = filledQty * fillPrice;
|
|
536
619
|
if (orderPlaced) {
|
|
537
620
|
try {
|
|
538
621
|
await applyProtectiveOrders({
|
|
539
622
|
connector,
|
|
540
623
|
symbol,
|
|
541
624
|
direction,
|
|
542
|
-
qty:
|
|
625
|
+
qty: resultingQty,
|
|
543
626
|
takeProfits,
|
|
544
627
|
stopLossPrice
|
|
545
628
|
});
|
|
546
629
|
} catch (error) {
|
|
547
630
|
await connector.closePosition({
|
|
548
631
|
symbol,
|
|
549
|
-
price:
|
|
632
|
+
price: resultingEntryPrice,
|
|
550
633
|
timestamp,
|
|
551
634
|
direction,
|
|
552
635
|
signal
|
|
@@ -559,7 +642,7 @@ var executeEntryOrder = async ({
|
|
|
559
642
|
if (orderPlaced) {
|
|
560
643
|
signal.orderFailureReason = void 0;
|
|
561
644
|
}
|
|
562
|
-
if (orderPlaced && recordRuntimeTrade) {
|
|
645
|
+
if (orderPlaced && recordRuntimeTrade && !isPositionIncrease) {
|
|
563
646
|
await recordRuntimeTradeOpen({
|
|
564
647
|
userName,
|
|
565
648
|
orderId,
|
|
@@ -568,8 +651,8 @@ var executeEntryOrder = async ({
|
|
|
568
651
|
symbol,
|
|
569
652
|
interval: signal.interval,
|
|
570
653
|
direction,
|
|
571
|
-
qty:
|
|
572
|
-
entryPrice,
|
|
654
|
+
qty: resultingQty,
|
|
655
|
+
entryPrice: resultingEntryPrice,
|
|
573
656
|
signalTimestamp,
|
|
574
657
|
signalClosePrice,
|
|
575
658
|
arrivalSnapshotTime: arrivalSnapshot.arrivalSnapshotTime,
|
|
@@ -580,7 +663,7 @@ var executeEntryOrder = async ({
|
|
|
580
663
|
spreadBps: arrivalSnapshot.spreadBps,
|
|
581
664
|
orderSubmitTime,
|
|
582
665
|
orderAckTime,
|
|
583
|
-
fillAvgPrice:
|
|
666
|
+
fillAvgPrice: fillPrice,
|
|
584
667
|
fillSource,
|
|
585
668
|
fillTime,
|
|
586
669
|
telemetryQuality: resolveRuntimeTelemetryQuality({
|
|
@@ -588,7 +671,7 @@ var executeEntryOrder = async ({
|
|
|
588
671
|
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
589
672
|
orderSubmitTime,
|
|
590
673
|
orderAckTime,
|
|
591
|
-
fillAvgPrice:
|
|
674
|
+
fillAvgPrice: fillPrice,
|
|
592
675
|
fillTime
|
|
593
676
|
}),
|
|
594
677
|
fee: estimatedOpenFee,
|
|
@@ -604,10 +687,26 @@ var executeEntryOrder = async ({
|
|
|
604
687
|
...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {}
|
|
605
688
|
});
|
|
606
689
|
}
|
|
607
|
-
if (
|
|
690
|
+
if (orderPlaced && recordRuntimeTrade && isPositionIncrease) {
|
|
691
|
+
await recordRuntimeTradeIncrease({
|
|
692
|
+
userName,
|
|
693
|
+
strategy: signal.strategy,
|
|
694
|
+
symbol,
|
|
695
|
+
direction,
|
|
696
|
+
resultingQty,
|
|
697
|
+
resultingEntryPrice,
|
|
698
|
+
addedQty: filledQty,
|
|
699
|
+
addedEntryPrice: fillPrice,
|
|
700
|
+
entryTimestamp: timestamp,
|
|
701
|
+
fee: estimatedOpenFee,
|
|
702
|
+
accountId: signal.accountId,
|
|
703
|
+
deploymentId: signal.deploymentId
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
if (hasUsableCurrentPosition && currentPosition?.price) {
|
|
608
707
|
return currentPosition.price;
|
|
609
708
|
}
|
|
610
|
-
return
|
|
709
|
+
return resultingEntryPrice;
|
|
611
710
|
};
|
|
612
711
|
var updatePositionProtection = async ({
|
|
613
712
|
connector,
|
|
@@ -1354,6 +1453,7 @@ var executeEntryDecision = async ({
|
|
|
1354
1453
|
timestamp: decision.entryContext.timestamp,
|
|
1355
1454
|
takeProfits: decision.orderPlan.takeProfits,
|
|
1356
1455
|
stopLossPrice: decision.orderPlan.stopLossPrice,
|
|
1456
|
+
positionIntent: decision.orderPlan.positionIntent,
|
|
1357
1457
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {},
|
|
1358
1458
|
signal,
|
|
1359
1459
|
beforePlaceOrder,
|
|
@@ -1385,6 +1485,7 @@ var executeEntryDecision = async ({
|
|
|
1385
1485
|
price: decision.entryContext.prices.currentPrice,
|
|
1386
1486
|
timestamp: decision.entryContext.timestamp,
|
|
1387
1487
|
direction: decision.entryContext.direction,
|
|
1488
|
+
positionIntent: decision.orderPlan.positionIntent,
|
|
1388
1489
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {}
|
|
1389
1490
|
});
|
|
1390
1491
|
if (!orderPlaced) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tradejs/node",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "Node-only runtime for the TradeJS TypeScript framework: strategies, backtests, Pine strategy loading, and plugin registries.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tradejs",
|
|
@@ -67,9 +67,9 @@
|
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@langchain/core": "^1.1.42",
|
|
69
69
|
"@langchain/openai": "^1.4.5",
|
|
70
|
-
"@tradejs/core": "^2.0.
|
|
71
|
-
"@tradejs/infra": "^2.0.
|
|
72
|
-
"@tradejs/types": "^2.0.
|
|
70
|
+
"@tradejs/core": "^2.0.4",
|
|
71
|
+
"@tradejs/infra": "^2.0.4",
|
|
72
|
+
"@tradejs/types": "^2.0.4",
|
|
73
73
|
"chalk": "4.1.2",
|
|
74
74
|
"ioredis": "5.8.0",
|
|
75
75
|
"pinets": "0.8.12",
|