@tradejs/strategies 1.0.6 → 1.0.8
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/chunk-37ZWRG3W.mjs +128 -0
- package/dist/chunk-H4MHFD4B.mjs +62 -0
- package/dist/{chunk-MOBKL73M.mjs → chunk-IMLNXICX.mjs} +183 -41
- package/dist/{chunk-XMRB45ZO.mjs → chunk-QVWMBYYM.mjs} +92 -45
- package/dist/chunk-SOVTOGY4.mjs +163 -0
- package/dist/{chunk-H2TU2YMA.mjs → chunk-TDUTYEGH.mjs} +111 -76
- package/dist/{chunk-GNQJ5TVU.mjs → chunk-UK4YHD2E.mjs} +34 -38
- package/dist/index.d.mts +294 -13
- package/dist/index.d.ts +294 -13
- package/dist/index.js +1825 -906
- package/dist/index.mjs +51 -18
- package/dist/{strategy-FYNNJDOH.mjs → strategy-ABIO65CR.mjs} +2 -33
- package/dist/{strategy-AFIGEHDS.mjs → strategy-D7H5J3C4.mjs} +1 -71
- package/dist/{strategy-LC2FSFVN.mjs → strategy-HQIPCUOY.mjs} +1 -72
- package/dist/{strategy-Y4SOK6FF.mjs → strategy-JQIJILHQ.mjs} +28 -109
- package/dist/strategy-QEIPAPY4.mjs +373 -0
- package/dist/{strategy-6TS2NFSC.mjs → strategy-WYN4FZ5S.mjs} +2 -125
- package/dist/{strategy-OI4WRB3S.mjs → strategy-XTUKPYPM.mjs} +466 -120
- package/package.json +5 -5
- package/dist/chunk-3PN7ZSJJ.mjs +0 -27
- package/dist/chunk-RDK2JK3K.mjs +0 -28
|
@@ -338,29 +338,43 @@ var buildTrendlineTimingContext = ({
|
|
|
338
338
|
|
|
339
339
|
// src/TrendLine/adapters/ai.ts
|
|
340
340
|
var TRENDLINE_CONTEXT_PROMPT = `
|
|
341
|
-
|
|
342
|
-
-
|
|
343
|
-
-
|
|
344
|
-
-
|
|
345
|
-
-
|
|
346
|
-
-
|
|
347
|
-
-
|
|
348
|
-
-
|
|
349
|
-
-
|
|
350
|
-
-
|
|
351
|
-
-
|
|
352
|
-
-
|
|
353
|
-
-
|
|
354
|
-
-
|
|
355
|
-
-
|
|
356
|
-
-
|
|
341
|
+
TrendLine addon:
|
|
342
|
+
- This setup is based on breakout or reaction around a trendline. 'payload.figures.trendline' contains the line geometry, and 'payload.additionalIndicators.trendlineContext' contains a compact summary of price location versus the line.
|
|
343
|
+
- For TrendLine, geometry and price structure have higher priority than indicator confirmation.
|
|
344
|
+
- Touches strengthen a line but do not confirm the signal by themselves. Without a confirmed breakout or retest, do not raise quality only because there were many touches.
|
|
345
|
+
- For SHORT on rising support ('trendline.mode="lows"'), you usually need either a clear move below the line or a retest from below with rejection. If price remains above the line or directly on it, use 'direction=null' and usually 'quality <= 2'.
|
|
346
|
+
- For LONG on descending resistance ('trendline.mode="highs"'), the mirror logic applies: you usually need a move above the line or a retest from above. If price remains below the line or directly on it, use 'direction=null' and usually 'quality <= 2'.
|
|
347
|
+
- If 'payload.additionalIndicators.trendlineContext.nearLineNoise=true', do not treat that as a confirmed breakout. Quality is usually '<= 2-3', and a retest or confirmation is still needed.
|
|
348
|
+
- If 'payload.additionalIndicators.trendlineContext.coinBiasAligned=false' or 'btcBiasAligned=false', treat it as a direct conflict with the signal direction. In that case, the signal is usually not confirmed unless the structural edge is exceptional.
|
|
349
|
+
- If 'payload.additionalIndicators.derivativesContext' exists, use it as Coinalyze-based breakout confirmation or conflict: open interest should support the move, funding should not be extremely crowded against the entry quality, and liquidation spikes can indicate flush, squeeze, or exhaustion.
|
|
350
|
+
- If 'derivativesContext.summary.riskFlags' contains 'oi_not_confirming', treat it as a direct sign that open interest does not confirm the breakout yet. Without very strong follow-through, do not elevate the signal to immediate entry.
|
|
351
|
+
- For SHORT during 'off_hours' or session overlap, require cleaner structural follow-through than during normal hours; those windows are noisier and less suitable for immediate approval.
|
|
352
|
+
- If 'payload.additionalIndicators.trendlineContext.clearBreak=false' and price is still near the line, do not describe it as a clean breakout.
|
|
353
|
+
- If 'clearBreak=true' but 'trendlineContext.weakCleanBreak=true', treat it as a formally valid but too-weak breakout: structure has been touched, but displacement margin is still limited. This usually calls for follow-through or retest, not immediate confirmation.
|
|
354
|
+
- If 'clearBreak=true' but 'trendlineContext.compressedCleanBreak=true', treat it as a compressed breakout after a cluster of close touches on a short line. Even with a formal line exit, this still usually calls for follow-through or retest rather than immediate confirmation.
|
|
355
|
+
- If 'clearBreak=true', 'trendlineContext.breakVsAtrRatio < 0.5', and 'trendlineContext.weakBtcLedBreak=true', treat it as a weak BTC-led break without enough coin-specific follow-through. This usually calls for retest or extra confirmation rather than immediate confirmation.
|
|
356
|
+
- For LONG on descending resistance, if the line is very long, the move above it is still modest, and BTC only weakly supports the break, treat it as an early breakout without sufficient follow-through. That usually needs retest or confirmation rather than immediate confirmation.
|
|
357
|
+
- For TrendLine, quality 4-5 is only allowed when all of the following are true at once: 'clearBreak=true', 'nearLineNoise=false', 'coinBiasAligned=true', and 'btcBiasAligned=true'. If any one of these conditions is not met, do not set quality above 3.
|
|
358
|
+
- Rare exception: if 'trendlineContext.aggressivePreBreakPressure=true', this is an aggressive pre-break-pressure setup. In that case 'quality=4' is allowed even without 'clearBreak', but only as early structural confirmation and only when coin/BTC bias is not conflicting.
|
|
359
|
+
- Another rare exception: if 'trendlineContext.strongNearBreakPressure=true', this is a mature line with pressure already building in the signal direction and very strong aligned pressure from both the coin and BTC. In that case 'quality=4' is allowed even when 'nearLineNoise=true', but only as early structural confirmation on strong structure.
|
|
357
360
|
`;
|
|
358
361
|
var TRENDLINE_PAYLOAD_PROMPT = `
|
|
359
|
-
-
|
|
360
|
-
-
|
|
361
|
-
-
|
|
362
|
+
- 'payload.figures.trendline' contains the full trendline geometry without trimming so touches and structure can be evaluated.
|
|
363
|
+
- 'payload.additionalIndicators.trendlineContext' contains 'mode / touches / distance / currentLinePrice / priceVsLinePct / priceVsLineSide / clearBreak / nearLineNoise / coinMaBias / btcMaBias / maxAllowedQuality / approvalAllowedNow / hardBlockReasons'.
|
|
364
|
+
- It also includes 'atrPct / breakVsAtrRatio / coinMaSpreadPct / btcMaSpreadPct / aggressivePreBreakPressure / strongNearBreakPressure / weakCleanBreak / compressedCleanBreak / weakBtcLedBreak / weakLongFarBreak'.
|
|
365
|
+
- If 'payload.additionalIndicators.derivativesContext' exists, it contains Coinalyze-derived open interest, funding, and liquidation fields for the signal moment; do not treat 'stale' or 'missing_derivatives' as confirmation or conflict.
|
|
362
366
|
`;
|
|
363
367
|
var buildTrendlineContext = (signal) => {
|
|
368
|
+
const marketContext = signal.additionalIndicators && typeof signal.additionalIndicators.marketContext === "object" && signal.additionalIndicators.marketContext && !Array.isArray(signal.additionalIndicators.marketContext) ? signal.additionalIndicators.marketContext : null;
|
|
369
|
+
const tradingSession = marketContext && typeof marketContext.tradingSession === "object" && marketContext.tradingSession && !Array.isArray(marketContext.tradingSession) ? marketContext.tradingSession : null;
|
|
370
|
+
const sessionPrimary = typeof tradingSession?.primarySession === "string" ? tradingSession.primarySession : null;
|
|
371
|
+
const sessionIsOverlap = tradingSession?.isOverlap === true;
|
|
372
|
+
const derivativesContext = signal.additionalIndicators && typeof signal.additionalIndicators.derivativesContext === "object" && signal.additionalIndicators.derivativesContext && !Array.isArray(signal.additionalIndicators.derivativesContext) ? signal.additionalIndicators.derivativesContext : null;
|
|
373
|
+
const derivativesSummary = derivativesContext && typeof derivativesContext.summary === "object" && derivativesContext.summary && !Array.isArray(derivativesContext.summary) ? derivativesContext.summary : null;
|
|
374
|
+
const derivativesRiskFlags = Array.isArray(derivativesSummary?.riskFlags) ? derivativesSummary.riskFlags.filter(
|
|
375
|
+
(flag) => typeof flag === "string" && flag.length > 0
|
|
376
|
+
) : [];
|
|
377
|
+
const oiNotConfirming = derivativesRiskFlags.includes("oi_not_confirming");
|
|
364
378
|
const structural = buildTrendlineStructuralContext(signal);
|
|
365
379
|
const trendLine = getTrendLineFromPayload(signal);
|
|
366
380
|
const coinMaFast = getLastFiniteNumber(signal.indicators?.maFast);
|
|
@@ -382,6 +396,12 @@ var buildTrendlineContext = (signal) => {
|
|
|
382
396
|
if (weakBtcLedBreak) {
|
|
383
397
|
hardBlockReasons.push("weak_btc_led_break");
|
|
384
398
|
}
|
|
399
|
+
if (oiNotConfirming && !hardBlockReasons.includes("oi_not_confirming")) {
|
|
400
|
+
hardBlockReasons.push("oi_not_confirming");
|
|
401
|
+
}
|
|
402
|
+
if (structural.signalDirection === "SHORT" && (entryTiming === "ready_follow_through" || entryTiming === "ready_retest") && (sessionPrimary === "off_hours" || sessionIsOverlap) && !hardBlockReasons.includes("short_session_risk")) {
|
|
403
|
+
hardBlockReasons.push("short_session_risk");
|
|
404
|
+
}
|
|
385
405
|
const deterministicQuality = getDeterministicTrendlineQuality({
|
|
386
406
|
signalDirection: structural.signalDirection,
|
|
387
407
|
clearBreak: structural.clearBreak,
|
|
@@ -410,6 +430,10 @@ var buildTrendlineContext = (signal) => {
|
|
|
410
430
|
aggressivePreBreakPressure,
|
|
411
431
|
strongNearBreakPressure,
|
|
412
432
|
weakBtcLedBreak,
|
|
433
|
+
sessionPrimary,
|
|
434
|
+
sessionIsOverlap,
|
|
435
|
+
derivativesRiskFlags,
|
|
436
|
+
oiNotConfirming,
|
|
413
437
|
deterministicQuality,
|
|
414
438
|
maxAllowedQuality,
|
|
415
439
|
approvalAllowedNow,
|
|
@@ -425,21 +449,25 @@ var formatPromptNumber = (value, fractionDigits = 4) => {
|
|
|
425
449
|
var getHardBlockReasonText = (reason) => {
|
|
426
450
|
switch (reason) {
|
|
427
451
|
case "no_clear_break":
|
|
428
|
-
return "
|
|
452
|
+
return "there is no clean breakout of the line";
|
|
429
453
|
case "near_line_noise":
|
|
430
|
-
return "
|
|
454
|
+
return "price is too close to the line and looks like noise";
|
|
431
455
|
case "coin_bias_conflict":
|
|
432
|
-
return "bias
|
|
456
|
+
return "coin bias conflicts with the direction";
|
|
433
457
|
case "btc_bias_conflict":
|
|
434
|
-
return "BTC
|
|
458
|
+
return "BTC context conflicts with the direction";
|
|
435
459
|
case "weak_clean_break":
|
|
436
|
-
return "
|
|
460
|
+
return "the formal breakout exists, but displacement is still too weak relative to ATR";
|
|
437
461
|
case "compressed_clean_break":
|
|
438
|
-
return "
|
|
462
|
+
return "the breakout looks too compressed: clustered close touches on a short line without enough follow-through";
|
|
439
463
|
case "weak_btc_led_break":
|
|
440
|
-
return "
|
|
464
|
+
return "the breakout is too small relative to ATR and looks more like a BTC-led move without enough coin follow-through";
|
|
441
465
|
case "weak_long_far_break":
|
|
442
|
-
return "
|
|
466
|
+
return "for LONG, the breakout of the very long line is still too modest and BTC support is too weak";
|
|
467
|
+
case "oi_not_confirming":
|
|
468
|
+
return "open interest does not confirm the move, so the breakout still looks unconfirmed on derivatives context";
|
|
469
|
+
case "short_session_risk":
|
|
470
|
+
return "for SHORT, the current session is too noisy or thin (off-hours or overlap), so clearer follow-through is required";
|
|
443
471
|
default:
|
|
444
472
|
return reason;
|
|
445
473
|
}
|
|
@@ -474,7 +502,8 @@ var getDeterministicTrendlineQuality = (trendlineContext) => {
|
|
|
474
502
|
const shortLineStrengthQuality4 = breakVsAtrRatio >= 0.6 && priceVsLinePctAbs >= 0.65 && touches >= 6 && distance < 120 && btcMaSpreadPct >= 0.75;
|
|
475
503
|
const matureLineQuality4 = breakVsAtrRatio >= 0.8 && priceVsLinePctAbs >= 0.7 && touches >= 5 && distance < 350 && btcMaSpreadPct >= 0.4;
|
|
476
504
|
const extendedHighConvictionQuality4 = breakVsAtrRatio >= 0.75 && priceVsLinePctAbs >= 0.65 && touches >= 5 && distance < 600 && btcMaSpreadPct >= 0.9;
|
|
477
|
-
|
|
505
|
+
const alignedRecentFollowThroughQuality4 = (entryTiming === "ready_follow_through" || entryTiming === "ready_retest") && breakVsAtrRatio >= 0.58 && breakVsAtrRatio <= 0.72 && priceVsLinePctAbs >= 0.48 && priceVsLinePctAbs <= 0.7 && touches >= 5 && distance <= 420 && btcMaSpreadPct >= 0.45 && coinMaSpreadPct >= 0.25;
|
|
506
|
+
return compactBreakoutQuality4 || shortLineStrengthQuality4 || matureLineQuality4 || extendedHighConvictionQuality4 || alignedRecentFollowThroughQuality4 ? 4 : 3;
|
|
478
507
|
}
|
|
479
508
|
const quality5 = breakVsAtrRatio >= 5 && priceVsLinePctAbs >= 10 && touches >= 5 && distance >= 240 && distance <= 400 && btcMaSpreadPct <= -1;
|
|
480
509
|
if (quality5) {
|
|
@@ -482,19 +511,23 @@ var getDeterministicTrendlineQuality = (trendlineContext) => {
|
|
|
482
511
|
}
|
|
483
512
|
const quality4 = breakVsAtrRatio >= 1 && breakVsAtrRatio < 2.5 && priceVsLinePctAbs >= 1 && touches >= 5 && distance < 300 && btcMaSpreadPct <= -0.5;
|
|
484
513
|
const strongReadyBreakoutQuality4 = entryTiming === "ready_breakout" && breakVsAtrRatio >= 2 && priceVsLinePctAbs >= 1.8 && touches >= 5 && btcMaSpreadPct <= -1 && (coinMaSpreadPct <= -1 || breakVsAtrRatio >= 3);
|
|
485
|
-
|
|
514
|
+
const moderateReadyBreakoutQuality4 = entryTiming === "ready_breakout" && breakVsAtrRatio >= 0.65 && breakVsAtrRatio <= 1.2 && priceVsLinePctAbs >= 0.65 && priceVsLinePctAbs <= 1 && touches >= 5 && distance >= 150 && distance <= 320 && btcMaSpreadPct <= -0.05 && coinMaSpreadPct <= -0.25;
|
|
515
|
+
if ((quality4 || moderateReadyBreakoutQuality4) && entryTiming === "ready_breakout") {
|
|
516
|
+
return 4;
|
|
517
|
+
}
|
|
518
|
+
return strongReadyBreakoutQuality4 ? 4 : 3;
|
|
486
519
|
};
|
|
487
520
|
var getDeterministicTrendlineQualityReason = (trendlineContext) => {
|
|
488
521
|
if (trendlineContext.hardBlockReasons.length > 0) {
|
|
489
|
-
return `TrendLine guardrail:
|
|
522
|
+
return `TrendLine guardrail: entry is blocked because ${trendlineContext.hardBlockReasons.map(getHardBlockReasonText).join("; ")}.`;
|
|
490
523
|
}
|
|
491
524
|
if (trendlineContext.signalDirection === "LONG") {
|
|
492
|
-
return "TrendLine deterministic quality:
|
|
525
|
+
return "TrendLine deterministic quality: the breakout exists, but LONG still lacks enough displacement, BTC support, or a compact enough line for immediate entry.";
|
|
493
526
|
}
|
|
494
527
|
if (trendlineContext.signalDirection === "SHORT") {
|
|
495
|
-
return "TrendLine deterministic quality:
|
|
528
|
+
return "TrendLine deterministic quality: the breakout exists, but SHORT still lacks enough bearish displacement or follow-through, so entry is still too early.";
|
|
496
529
|
}
|
|
497
|
-
return "TrendLine deterministic quality:
|
|
530
|
+
return "TrendLine deterministic quality: the structure is still not strong enough for entry right now.";
|
|
498
531
|
};
|
|
499
532
|
var getTrendlineContextFromPayload = (payload, signal) => {
|
|
500
533
|
const additional = payload.additionalIndicators;
|
|
@@ -503,25 +536,35 @@ var getTrendlineContextFromPayload = (payload, signal) => {
|
|
|
503
536
|
};
|
|
504
537
|
var trendLineAiAdapter = {
|
|
505
538
|
// Shared builder trims nested series/figures; TrendLine keeps trendline geometry untrimmed on purpose.
|
|
506
|
-
buildPayload: ({ signal, basePayload }) =>
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
...basePayload.
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
539
|
+
buildPayload: ({ signal, basePayload }) => {
|
|
540
|
+
const mergedAdditionalIndicators = {
|
|
541
|
+
...signal.additionalIndicators ?? {},
|
|
542
|
+
...basePayload.additionalIndicators ?? {}
|
|
543
|
+
};
|
|
544
|
+
const trendlineContext = buildTrendlineContext({
|
|
545
|
+
...signal,
|
|
546
|
+
additionalIndicators: mergedAdditionalIndicators
|
|
547
|
+
});
|
|
548
|
+
return {
|
|
549
|
+
...basePayload,
|
|
550
|
+
figures: {
|
|
551
|
+
...basePayload.figures,
|
|
552
|
+
// Keep raw line geometry available exactly where the shared prompt expects it.
|
|
553
|
+
trendline: getTrendLineFromPayload(signal)
|
|
554
|
+
},
|
|
555
|
+
additionalIndicators: {
|
|
556
|
+
...mergedAdditionalIndicators,
|
|
557
|
+
trendlineContext
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
},
|
|
518
561
|
postProcessAnalysis: ({ signal, payload, analysis }) => {
|
|
519
562
|
const trendlineContext = getTrendlineContextFromPayload(payload, signal);
|
|
520
563
|
const quality = trendlineContext.deterministicQuality;
|
|
521
564
|
const signalDirection = signal.direction === "LONG" || signal.direction === "SHORT" ? signal.direction : null;
|
|
522
565
|
if ((trendlineContext.aggressivePreBreakPressure === true || trendlineContext.strongNearBreakPressure === true) && signalDirection != null) {
|
|
523
|
-
const fallbackReason = trendlineContext.strongNearBreakPressure === true ? "TrendLine strong near-break pressure:
|
|
524
|
-
const fallbackComment = trendlineContext.strongNearBreakPressure === true ? "TrendLine strong near-break pressure:
|
|
566
|
+
const fallbackReason = trendlineContext.strongNearBreakPressure === true ? "TrendLine strong near-break pressure: a mature line is already compressing in the trade direction, so early entry is allowed by strategy code." : "TrendLine aggressive pre-break pressure: early structural confirmation is allowed under strong bearish pressure.";
|
|
567
|
+
const fallbackComment = trendlineContext.strongNearBreakPressure === true ? "TrendLine strong near-break pressure: early entry is allowed by strategy code." : "TrendLine aggressive pre-break pressure: early entry is allowed by strategy code.";
|
|
525
568
|
return {
|
|
526
569
|
...analysis,
|
|
527
570
|
direction: signalDirection,
|
|
@@ -552,17 +595,17 @@ var trendLineAiAdapter = {
|
|
|
552
595
|
const retestPrice = trendlineContext.currentLinePrice ?? analysis.retestPrice ?? null;
|
|
553
596
|
const qualityReason = mergeShortText(
|
|
554
597
|
getDeterministicTrendlineQualityReason(trendlineContext),
|
|
555
|
-
"TrendLine guardrail:
|
|
598
|
+
"TrendLine guardrail: entry is blocked until the structure is confirmed.",
|
|
556
599
|
400
|
|
557
600
|
);
|
|
558
601
|
const triggerInvalidation = mergeShortText(
|
|
559
|
-
trendlineContext.hardBlockReasons.length > 0 ?
|
|
560
|
-
"
|
|
602
|
+
trendlineContext.hardBlockReasons.length > 0 ? `Wait for a clean breakout or retest of the line and resolve the conflicts: ${trendlineContext.hardBlockReasons.map(getHardBlockReasonText).join("; ")}.` : "Wait for stronger breakout follow-through or a line retest confirmed by both the coin and BTC.",
|
|
603
|
+
"Wait for a clean line breakout or retest plus confirmation from the coin and BTC.",
|
|
561
604
|
400
|
|
562
605
|
);
|
|
563
606
|
const comment = mergeShortText(
|
|
564
|
-
trendlineContext.hardBlockReasons.length > 0 ? `TrendLine guardrail
|
|
565
|
-
"TrendLine guardrail
|
|
607
|
+
trendlineContext.hardBlockReasons.length > 0 ? `TrendLine guardrail blocked the entry: ${trendlineContext.hardBlockReasons.map(getHardBlockReasonText).join("; ")}.` : "TrendLine deterministic quality downgraded the entry to watch or reject until stronger structure appears.",
|
|
608
|
+
"TrendLine guardrail blocked the entry until the structure is confirmed.",
|
|
566
609
|
1024
|
|
567
610
|
);
|
|
568
611
|
return {
|
|
@@ -575,12 +618,12 @@ var trendLineAiAdapter = {
|
|
|
575
618
|
stopLossPrice: null,
|
|
576
619
|
setup: mergeShortText(
|
|
577
620
|
analysis.setup ?? "",
|
|
578
|
-
"
|
|
621
|
+
"There is no confirmed trendline breakout or retest for entry right now.",
|
|
579
622
|
400
|
|
580
623
|
),
|
|
581
624
|
retestPlan: mergeShortText(
|
|
582
625
|
analysis.retestPlan ?? "",
|
|
583
|
-
"
|
|
626
|
+
"Wait for a return to the line and a reaction in the trade direction before a new entry.",
|
|
584
627
|
400
|
|
585
628
|
),
|
|
586
629
|
qualityReason,
|
|
@@ -600,7 +643,7 @@ ${TRENDLINE_PAYLOAD_PROMPT}
|
|
|
600
643
|
}
|
|
601
644
|
return `
|
|
602
645
|
|
|
603
|
-
|
|
646
|
+
Additional TrendLine context:
|
|
604
647
|
- trendline.mode=${trendlineContext.mode ?? "n/a"}
|
|
605
648
|
- trendline.touches=${formatPromptNumber(trendlineContext.touches, 0)}
|
|
606
649
|
- trendline.distance=${formatPromptNumber(trendlineContext.distance, 0)}
|
|
@@ -634,18 +677,18 @@ ${TRENDLINE_PAYLOAD_PROMPT}
|
|
|
634
677
|
- btc.maSpreadPct=${formatPromptNumber(trendlineContext.btcMaSpreadPct, 3)}%
|
|
635
678
|
- btc.biasAligned=${String(trendlineContext.btcBiasAligned)}
|
|
636
679
|
|
|
637
|
-
|
|
638
|
-
- SHORT
|
|
639
|
-
- LONG
|
|
640
|
-
-
|
|
641
|
-
-
|
|
642
|
-
-
|
|
643
|
-
-
|
|
644
|
-
-
|
|
645
|
-
-
|
|
646
|
-
-
|
|
647
|
-
-
|
|
648
|
-
-
|
|
680
|
+
Interpretation rules for TrendLine:
|
|
681
|
+
- SHORT from a 'lows' line is confirmed only by a clear move below the line or a retest from below with rejection.
|
|
682
|
+
- LONG from a 'highs' line is confirmed only by a clear move above the line or a retest from above with rejection.
|
|
683
|
+
- If 'trendline.nearLineNoise=true' or any bias alignment is false, it is better to return 'direction=null' and quality 1-3 than to describe the signal as confirmed without margin.
|
|
684
|
+
- If 'trendline.weakCleanBreak=true', the formal breakout exists but is too weak in displacement; it needs follow-through or retest rather than quality 4-5.
|
|
685
|
+
- If 'trendline.compressedCleanBreak=true', the breakout exists formally, but the line is too short and compressed after clustered touches; this usually needs follow-through or retest rather than immediate confirmation.
|
|
686
|
+
- If 'trendline.weakBtcLedBreak=true', treat it as a small breakout driven more by BTC than by the coin itself; this usually calls for retest and quality 1-3.
|
|
687
|
+
- If 'clearBreak=false' or any alignment is false, do not raise quality above 3.
|
|
688
|
+
- If 'trendline.aggressivePreBreakPressure=true', an early SHORT before a clear breakout may be considered only as an exception: quality can be at most 4, and the explanation must clearly state that the structural confirmation is still aggressive.
|
|
689
|
+
- If 'trendline.strongNearBreakPressure=true', an early SHORT may be considered when pressure is already strong on the correct side of the line even if the breakout still falls short of the 'clearBreak' threshold: quality can be at most 4.
|
|
690
|
+
- The strategy deterministically normalizes final quality to 'trendline.deterministicQuality'; your job is to explain the decision within that frame, not to argue with the tier.
|
|
691
|
+
- If 'trendline.approvalAllowedNow=false', do not describe the signal as fully confirmed right now; explain what is still missing for confirmation.
|
|
649
692
|
`;
|
|
650
693
|
},
|
|
651
694
|
mapEntryRuntimeFromConfig: (config2) => mapAiRuntimeFromConfig(
|
|
@@ -689,18 +732,9 @@ var trendLineMlAdapter = {
|
|
|
689
732
|
})
|
|
690
733
|
};
|
|
691
734
|
|
|
692
|
-
// src/TrendLine/hooks.ts
|
|
693
|
-
import { createCloseOppositeBeforePlaceOrderHook } from "@tradejs/node/strategies";
|
|
694
|
-
var trendLineBeforePlaceOrderHook = createCloseOppositeBeforePlaceOrderHook({
|
|
695
|
-
isEnabled: (config2) => Boolean(config2.CLOSE_OPPOSITE_POSITIONS)
|
|
696
|
-
});
|
|
697
|
-
|
|
698
735
|
// src/TrendLine/manifest.ts
|
|
699
736
|
var trendLineManifest = {
|
|
700
737
|
name: "TrendLine",
|
|
701
|
-
hooks: {
|
|
702
|
-
beforePlaceOrder: trendLineBeforePlaceOrderHook
|
|
703
|
-
},
|
|
704
738
|
aiAdapter: trendLineAiAdapter,
|
|
705
739
|
mlAdapter: trendLineMlAdapter
|
|
706
740
|
};
|
|
@@ -713,6 +747,7 @@ var config = {
|
|
|
713
747
|
CLOSE_OPPOSITE_POSITIONS: false,
|
|
714
748
|
BACKTEST_PRICE_MODE: "mid",
|
|
715
749
|
AI_ENABLED: false,
|
|
750
|
+
AI_MODE: "llm",
|
|
716
751
|
ML_ENABLED: false,
|
|
717
752
|
ML_THRESHOLD: 0.1,
|
|
718
753
|
MIN_AI_QUALITY: 3,
|
|
@@ -387,23 +387,23 @@ var buildReverseTrendlineTimingContext = ({
|
|
|
387
387
|
|
|
388
388
|
// src/ReverseTrendLine/adapters/ai.ts
|
|
389
389
|
var REVERSE_TRENDLINE_CONTEXT_PROMPT = `
|
|
390
|
-
|
|
391
|
-
-
|
|
392
|
-
-
|
|
393
|
-
-
|
|
394
|
-
-
|
|
395
|
-
-
|
|
396
|
-
-
|
|
397
|
-
-
|
|
398
|
-
-
|
|
399
|
-
-
|
|
400
|
-
-
|
|
401
|
-
-
|
|
402
|
-
-
|
|
390
|
+
ReverseTrendLine addon:
|
|
391
|
+
- This is a trendline bounce strategy, not a breakout strategy.
|
|
392
|
+
- For LONG on a support line (\`trendline.mode="lows"\`), you need a touch or false break of the line followed by a close back above it.
|
|
393
|
+
- For SHORT on a resistance line (\`trendline.mode="highs"\`), you need a touch or false break of the line followed by a close back below it.
|
|
394
|
+
- If price has already broken through the line with conviction in the opposite direction, this is not a bounce setup: use \`direction=null\` and \`quality <= 2\`.
|
|
395
|
+
- For bounce setups, prioritize candle reaction at the line, rejection wick quality, a close on the correct side, and next-bar follow-through.
|
|
396
|
+
- If \`payload.additionalIndicators.reverseTrendlineContext.failedBounceBreak=true\`, do not treat the signal as structurally confirmed.
|
|
397
|
+
- If \`payload.additionalIndicators.reverseTrendlineContext.entryTiming\` is not \`ready_rejection\` or \`ready_follow_through\`, quality is usually \`<= 3\`.
|
|
398
|
+
- Baseline deterministic approval for same-bar rejection is intentionally strict:
|
|
399
|
+
- a strong conflict-only rejection may qualify for \`quality=4\`;
|
|
400
|
+
- some same-bar rejections with \`conflictState=none\` or \`both\` may reach \`quality=4\` only with a very strong deterministic rejection score.
|
|
401
|
+
- For SHORT bounce setups with \`btc_bias_conflict\`, do not overstate quality; those cases usually stay in watch mode unless the structural confirmation is much stronger.
|
|
402
|
+
- If \`deterministicRejectionScore\` is low or medium, do not assign \`quality=4\` just because the candle visually resembles a rejection.
|
|
403
403
|
`;
|
|
404
404
|
var REVERSE_TRENDLINE_PAYLOAD_PROMPT = `
|
|
405
|
-
-
|
|
406
|
-
-
|
|
405
|
+
- \`payload.figures.trendline\` contains the line geometry.
|
|
406
|
+
- \`payload.additionalIndicators.reverseTrendlineContext\` contains a compact bounce summary: direction, price distance to the line, whether the line was touched, whether there was a rejection candle, rejection strength, timing stage, bias conflicts, and \`deterministicRejectionScore\`.
|
|
407
407
|
`;
|
|
408
408
|
var getReverseTrendlineBiasConflictState = (context) => {
|
|
409
409
|
const coinConflict = context.coinBiasAligned === false;
|
|
@@ -444,12 +444,16 @@ var getDeterministicReverseTrendlineQuality = (context) => {
|
|
|
444
444
|
if (quality4FollowThrough) {
|
|
445
445
|
return 4;
|
|
446
446
|
}
|
|
447
|
-
const quality4ConflictRejection = context.entryTiming === "ready_rejection" && conflictOnly && rejectionStrengthPct >= 0.45 && touches >= 5 && !(context.signalDirection === "SHORT" && biasConflictState === "btc_only");
|
|
447
|
+
const quality4ConflictRejection = context.entryTiming === "ready_rejection" && conflictOnly && rejectionStrengthPct >= 0.45 && touches >= 5 && !(context.signalDirection === "SHORT" && biasConflictState === "coin_only" && distance <= 180 && rejectionWickPct <= 0.45) && !(context.signalDirection === "SHORT" && biasConflictState === "btc_only");
|
|
448
448
|
if (quality4ConflictRejection) {
|
|
449
449
|
return 4;
|
|
450
450
|
}
|
|
451
451
|
const rejectionScore = getDeterministicReverseTrendlineRejectionScore(context);
|
|
452
|
-
const
|
|
452
|
+
const quality4EliteShortBtcOnlyRejection = context.entryTiming === "ready_rejection" && context.signalDirection === "SHORT" && biasConflictState === "btc_only" && rejectionScore != null && rejectionScore >= 5 && rejectionWickPct >= 0.6 && touches >= 5 && distance <= 200;
|
|
453
|
+
if (quality4EliteShortBtcOnlyRejection) {
|
|
454
|
+
return 4;
|
|
455
|
+
}
|
|
456
|
+
const quality4ScoredRejection = context.entryTiming === "ready_rejection" && (biasConflictState === "none" || biasConflictState === "both") && rejectionScore != null && rejectionScore >= 7 && !(context.signalDirection === "SHORT" && biasConflictState === "none" && distance <= 150 && (rejectionWickPct >= 0.7 || rejectionStrengthPct >= 1.3));
|
|
453
457
|
if (quality4ScoredRejection) {
|
|
454
458
|
return 4;
|
|
455
459
|
}
|
|
@@ -536,11 +540,11 @@ var getReverseTrendlineContextFromPayload = (payload, signal) => {
|
|
|
536
540
|
var getHardBlockReasonText = (reason) => {
|
|
537
541
|
switch (reason) {
|
|
538
542
|
case "failed_bounce_break":
|
|
539
|
-
return "
|
|
543
|
+
return "price broke through the line against the intended bounce";
|
|
540
544
|
case "coin_bias_conflict":
|
|
541
|
-
return "bias
|
|
545
|
+
return "coin bias conflicts with the bounce direction";
|
|
542
546
|
case "btc_bias_conflict":
|
|
543
|
-
return "BTC
|
|
547
|
+
return "BTC context conflicts with the bounce direction";
|
|
544
548
|
default:
|
|
545
549
|
return reason;
|
|
546
550
|
}
|
|
@@ -579,9 +583,9 @@ var reverseTrendLineAiAdapter = {
|
|
|
579
583
|
retestPrice: context.currentLinePrice ?? null,
|
|
580
584
|
takeProfitPrice: null,
|
|
581
585
|
stopLossPrice: null,
|
|
582
|
-
qualityReason: context.hardBlockReasons.length > 0 ? `ReverseTrendLine guardrail: ${context.hardBlockReasons.map(getHardBlockReasonText).join("; ")}.` : "ReverseTrendLine deterministic quality
|
|
583
|
-
triggerInvalidation: context.hardBlockReasons.length > 0 ?
|
|
584
|
-
comment: context.hardBlockReasons.length > 0 ? `ReverseTrendLine guardrail
|
|
586
|
+
qualityReason: context.hardBlockReasons.length > 0 ? `ReverseTrendLine guardrail: ${context.hardBlockReasons.map(getHardBlockReasonText).join("; ")}.` : "ReverseTrendLine deterministic quality requires either a strong conflict-only rejection or a confirmed aligned follow-through for a bounce.",
|
|
587
|
+
triggerInvalidation: context.hardBlockReasons.length > 0 ? `Wait for a new bounce setup: ${context.hardBlockReasons.map(getHardBlockReasonText).join("; ")}.` : "Wait for a line touch, a rejection candle, and a close held on the correct side of the line.",
|
|
588
|
+
comment: context.hardBlockReasons.length > 0 ? `ReverseTrendLine guardrail blocked the entry: ${context.hardBlockReasons.map(getHardBlockReasonText).join("; ")}.` : "ReverseTrendLine keeps the setup in watch mode until the bounce is confirmed."
|
|
585
589
|
};
|
|
586
590
|
},
|
|
587
591
|
buildSystemPromptAddon: () => `${REVERSE_TRENDLINE_CONTEXT_PROMPT}
|
|
@@ -590,7 +594,7 @@ ${REVERSE_TRENDLINE_PAYLOAD_PROMPT}`,
|
|
|
590
594
|
const context = getReverseTrendlineContextFromPayload(payload, signal);
|
|
591
595
|
return `
|
|
592
596
|
|
|
593
|
-
|
|
597
|
+
Additional ReverseTrendLine context:
|
|
594
598
|
- entryTiming=${context.entryTiming}
|
|
595
599
|
- lineTouchedNow=${context.lineTouchedNow}
|
|
596
600
|
- closeOnBounceSide=${context.closeOnBounceSide}
|
|
@@ -605,11 +609,11 @@ ${REVERSE_TRENDLINE_PAYLOAD_PROMPT}`,
|
|
|
605
609
|
- approvalAllowedNow=${context.approvalAllowedNow}
|
|
606
610
|
- hardBlockReasons=${context.hardBlockReasons.join(", ") || "none"}
|
|
607
611
|
|
|
608
|
-
|
|
609
|
-
-
|
|
610
|
-
-
|
|
611
|
-
-
|
|
612
|
-
-
|
|
612
|
+
Interpretation rules for ReverseTrendLine:
|
|
613
|
+
- look for structural confirmation of a reaction from the line, not a breakout through the line;
|
|
614
|
+
- if \`failedBounceBreak=true\` is already present, do not treat the signal as confirmed;
|
|
615
|
+
- if the setup is still in \`wait_touch\`, \`wait_reaction_confirmation\`, or \`stale_reaction\`, do not overstate quality;
|
|
616
|
+
- if \`deterministicRejectionScore\` is high, use it only as an extra signal together with the proper bounce context, not as a replacement for structure.
|
|
613
617
|
`;
|
|
614
618
|
},
|
|
615
619
|
mapEntryRuntimeFromConfig: (config2) => mapAiRuntimeFromConfig(
|
|
@@ -617,18 +621,9 @@ ${REVERSE_TRENDLINE_PAYLOAD_PROMPT}`,
|
|
|
617
621
|
)
|
|
618
622
|
};
|
|
619
623
|
|
|
620
|
-
// src/ReverseTrendLine/hooks.ts
|
|
621
|
-
import { createCloseOppositeBeforePlaceOrderHook } from "@tradejs/node/strategies";
|
|
622
|
-
var reverseTrendLineBeforePlaceOrderHook = createCloseOppositeBeforePlaceOrderHook({
|
|
623
|
-
isEnabled: (config2) => Boolean(config2.CLOSE_OPPOSITE_POSITIONS)
|
|
624
|
-
});
|
|
625
|
-
|
|
626
624
|
// src/ReverseTrendLine/manifest.ts
|
|
627
625
|
var reverseTrendLineManifest = {
|
|
628
626
|
name: "ReverseTrendLine",
|
|
629
|
-
hooks: {
|
|
630
|
-
beforePlaceOrder: reverseTrendLineBeforePlaceOrderHook
|
|
631
|
-
},
|
|
632
627
|
aiAdapter: reverseTrendLineAiAdapter
|
|
633
628
|
};
|
|
634
629
|
|
|
@@ -640,6 +635,7 @@ var config = {
|
|
|
640
635
|
CLOSE_OPPOSITE_POSITIONS: false,
|
|
641
636
|
BACKTEST_PRICE_MODE: "mid",
|
|
642
637
|
AI_ENABLED: false,
|
|
638
|
+
AI_MODE: "llm",
|
|
643
639
|
MIN_AI_QUALITY: 3,
|
|
644
640
|
FEE_PERCENT: 5e-3,
|
|
645
641
|
MAX_LOSS_VALUE: 10,
|