@tradejs/node 1.0.9 → 1.0.10
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/ai.d.mts +4 -1
- package/dist/ai.d.ts +4 -1
- package/dist/ai.js +601 -93
- package/dist/ai.mjs +5 -3
- package/dist/backtest.d.mts +26 -2
- package/dist/backtest.d.ts +26 -2
- package/dist/backtest.js +3368 -485
- package/dist/backtest.mjs +1839 -269
- package/dist/chunk-37VNDZVX.mjs +1040 -0
- package/dist/chunk-IUZML4RK.mjs +1136 -0
- package/dist/{chunk-WGOYR6AB.mjs → chunk-QVSMINLG.mjs} +1 -1
- package/dist/{chunk-JMDYEKIO.mjs → chunk-V3YMKE4I.mjs} +1 -1
- package/dist/{chunk-JU77QVJ3.mjs → chunk-WS5DYEVZ.mjs} +59 -5
- package/dist/cli.d.mts +13 -3
- package/dist/cli.d.ts +13 -3
- package/dist/cli.js +1016 -234
- package/dist/cli.mjs +338 -65
- package/dist/connectors.js +59 -5
- package/dist/connectors.mjs +2 -2
- package/dist/registry.js +59 -5
- package/dist/registry.mjs +2 -2
- package/dist/strategies.d.mts +29 -8
- package/dist/strategies.d.ts +29 -8
- package/dist/strategies.js +3417 -1285
- package/dist/strategies.mjs +892 -112
- package/package.json +6 -6
- package/dist/chunk-2JKX3DM7.mjs +0 -619
- package/dist/chunk-JRRG3YQG.mjs +0 -154
package/dist/cli.js
CHANGED
|
@@ -1361,11 +1361,11 @@ var require_lodash = __commonJS({
|
|
|
1361
1361
|
return isFunction(object[key]);
|
|
1362
1362
|
});
|
|
1363
1363
|
}
|
|
1364
|
-
function baseGet(object,
|
|
1365
|
-
|
|
1366
|
-
var index = 0, length =
|
|
1364
|
+
function baseGet(object, path4) {
|
|
1365
|
+
path4 = castPath(path4, object);
|
|
1366
|
+
var index = 0, length = path4.length;
|
|
1367
1367
|
while (object != null && index < length) {
|
|
1368
|
-
object = object[toKey(
|
|
1368
|
+
object = object[toKey(path4[index++])];
|
|
1369
1369
|
}
|
|
1370
1370
|
return index && index == length ? object : undefined2;
|
|
1371
1371
|
}
|
|
@@ -1429,10 +1429,10 @@ var require_lodash = __commonJS({
|
|
|
1429
1429
|
});
|
|
1430
1430
|
return accumulator;
|
|
1431
1431
|
}
|
|
1432
|
-
function baseInvoke(object,
|
|
1433
|
-
|
|
1434
|
-
object = parent(object,
|
|
1435
|
-
var func = object == null ? object : object[toKey(last(
|
|
1432
|
+
function baseInvoke(object, path4, args) {
|
|
1433
|
+
path4 = castPath(path4, object);
|
|
1434
|
+
object = parent(object, path4);
|
|
1435
|
+
var func = object == null ? object : object[toKey(last(path4))];
|
|
1436
1436
|
return func == null ? undefined2 : apply(func, object, args);
|
|
1437
1437
|
}
|
|
1438
1438
|
function baseIsArguments(value) {
|
|
@@ -1588,13 +1588,13 @@ var require_lodash = __commonJS({
|
|
|
1588
1588
|
return object === source || baseIsMatch(object, source, matchData);
|
|
1589
1589
|
};
|
|
1590
1590
|
}
|
|
1591
|
-
function baseMatchesProperty(
|
|
1592
|
-
if (isKey(
|
|
1593
|
-
return matchesStrictComparable(toKey(
|
|
1591
|
+
function baseMatchesProperty(path4, srcValue) {
|
|
1592
|
+
if (isKey(path4) && isStrictComparable(srcValue)) {
|
|
1593
|
+
return matchesStrictComparable(toKey(path4), srcValue);
|
|
1594
1594
|
}
|
|
1595
1595
|
return function(object) {
|
|
1596
|
-
var objValue = get(object,
|
|
1597
|
-
return objValue === undefined2 && objValue === srcValue ? hasIn(object,
|
|
1596
|
+
var objValue = get(object, path4);
|
|
1597
|
+
return objValue === undefined2 && objValue === srcValue ? hasIn(object, path4) : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);
|
|
1598
1598
|
};
|
|
1599
1599
|
}
|
|
1600
1600
|
function baseMerge(object, source, srcIndex, customizer, stack) {
|
|
@@ -1691,23 +1691,23 @@ var require_lodash = __commonJS({
|
|
|
1691
1691
|
});
|
|
1692
1692
|
}
|
|
1693
1693
|
function basePick(object, paths) {
|
|
1694
|
-
return basePickBy(object, paths, function(value,
|
|
1695
|
-
return hasIn(object,
|
|
1694
|
+
return basePickBy(object, paths, function(value, path4) {
|
|
1695
|
+
return hasIn(object, path4);
|
|
1696
1696
|
});
|
|
1697
1697
|
}
|
|
1698
1698
|
function basePickBy(object, paths, predicate) {
|
|
1699
1699
|
var index = -1, length = paths.length, result2 = {};
|
|
1700
1700
|
while (++index < length) {
|
|
1701
|
-
var
|
|
1702
|
-
if (predicate(value,
|
|
1703
|
-
baseSet(result2, castPath(
|
|
1701
|
+
var path4 = paths[index], value = baseGet(object, path4);
|
|
1702
|
+
if (predicate(value, path4)) {
|
|
1703
|
+
baseSet(result2, castPath(path4, object), value);
|
|
1704
1704
|
}
|
|
1705
1705
|
}
|
|
1706
1706
|
return result2;
|
|
1707
1707
|
}
|
|
1708
|
-
function basePropertyDeep(
|
|
1708
|
+
function basePropertyDeep(path4) {
|
|
1709
1709
|
return function(object) {
|
|
1710
|
-
return baseGet(object,
|
|
1710
|
+
return baseGet(object, path4);
|
|
1711
1711
|
};
|
|
1712
1712
|
}
|
|
1713
1713
|
function basePullAll(array, values2, iteratee2, comparator) {
|
|
@@ -1781,14 +1781,14 @@ var require_lodash = __commonJS({
|
|
|
1781
1781
|
var array = values(collection);
|
|
1782
1782
|
return shuffleSelf(array, baseClamp(n, 0, array.length));
|
|
1783
1783
|
}
|
|
1784
|
-
function baseSet(object,
|
|
1784
|
+
function baseSet(object, path4, value, customizer) {
|
|
1785
1785
|
if (!isObject(object)) {
|
|
1786
1786
|
return object;
|
|
1787
1787
|
}
|
|
1788
|
-
|
|
1789
|
-
var index = -1, length =
|
|
1788
|
+
path4 = castPath(path4, object);
|
|
1789
|
+
var index = -1, length = path4.length, lastIndex = length - 1, nested = object;
|
|
1790
1790
|
while (nested != null && ++index < length) {
|
|
1791
|
-
var key = toKey(
|
|
1791
|
+
var key = toKey(path4[index]), newValue = value;
|
|
1792
1792
|
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
1793
1793
|
return object;
|
|
1794
1794
|
}
|
|
@@ -1796,7 +1796,7 @@ var require_lodash = __commonJS({
|
|
|
1796
1796
|
var objValue = nested[key];
|
|
1797
1797
|
newValue = customizer ? customizer(objValue, key, nested) : undefined2;
|
|
1798
1798
|
if (newValue === undefined2) {
|
|
1799
|
-
newValue = isObject(objValue) ? objValue : isIndex(
|
|
1799
|
+
newValue = isObject(objValue) ? objValue : isIndex(path4[index + 1]) ? [] : {};
|
|
1800
1800
|
}
|
|
1801
1801
|
}
|
|
1802
1802
|
assignValue(nested, key, newValue);
|
|
@@ -1962,14 +1962,14 @@ var require_lodash = __commonJS({
|
|
|
1962
1962
|
}
|
|
1963
1963
|
return result2;
|
|
1964
1964
|
}
|
|
1965
|
-
function baseUnset(object,
|
|
1966
|
-
|
|
1967
|
-
var index = -1, length =
|
|
1965
|
+
function baseUnset(object, path4) {
|
|
1966
|
+
path4 = castPath(path4, object);
|
|
1967
|
+
var index = -1, length = path4.length;
|
|
1968
1968
|
if (!length) {
|
|
1969
1969
|
return true;
|
|
1970
1970
|
}
|
|
1971
1971
|
while (++index < length) {
|
|
1972
|
-
var key = toKey(
|
|
1972
|
+
var key = toKey(path4[index]);
|
|
1973
1973
|
if (key === "__proto__" && !hasOwnProperty.call(object, "__proto__")) {
|
|
1974
1974
|
return false;
|
|
1975
1975
|
}
|
|
@@ -1977,11 +1977,11 @@ var require_lodash = __commonJS({
|
|
|
1977
1977
|
return false;
|
|
1978
1978
|
}
|
|
1979
1979
|
}
|
|
1980
|
-
var obj = parent(object,
|
|
1981
|
-
return obj == null || delete obj[toKey(last(
|
|
1980
|
+
var obj = parent(object, path4);
|
|
1981
|
+
return obj == null || delete obj[toKey(last(path4))];
|
|
1982
1982
|
}
|
|
1983
|
-
function baseUpdate(object,
|
|
1984
|
-
return baseSet(object,
|
|
1983
|
+
function baseUpdate(object, path4, updater, customizer) {
|
|
1984
|
+
return baseSet(object, path4, updater(baseGet(object, path4)), customizer);
|
|
1985
1985
|
}
|
|
1986
1986
|
function baseWhile(array, predicate, isDrop, fromRight) {
|
|
1987
1987
|
var length = array.length, index = fromRight ? length : -1;
|
|
@@ -2864,11 +2864,11 @@ var require_lodash = __commonJS({
|
|
|
2864
2864
|
var match = source.match(reWrapDetails);
|
|
2865
2865
|
return match ? match[1].split(reSplitDetails) : [];
|
|
2866
2866
|
}
|
|
2867
|
-
function hasPath(object,
|
|
2868
|
-
|
|
2869
|
-
var index = -1, length =
|
|
2867
|
+
function hasPath(object, path4, hasFunc) {
|
|
2868
|
+
path4 = castPath(path4, object);
|
|
2869
|
+
var index = -1, length = path4.length, result2 = false;
|
|
2870
2870
|
while (++index < length) {
|
|
2871
|
-
var key = toKey(
|
|
2871
|
+
var key = toKey(path4[index]);
|
|
2872
2872
|
if (!(result2 = object != null && hasFunc(object, key))) {
|
|
2873
2873
|
break;
|
|
2874
2874
|
}
|
|
@@ -3070,8 +3070,8 @@ var require_lodash = __commonJS({
|
|
|
3070
3070
|
return apply(func, this, otherArgs);
|
|
3071
3071
|
};
|
|
3072
3072
|
}
|
|
3073
|
-
function parent(object,
|
|
3074
|
-
return
|
|
3073
|
+
function parent(object, path4) {
|
|
3074
|
+
return path4.length < 2 ? object : baseGet(object, baseSlice(path4, 0, -1));
|
|
3075
3075
|
}
|
|
3076
3076
|
function reorder(array, indexes) {
|
|
3077
3077
|
var arrLength = array.length, length = nativeMin(indexes.length, arrLength), oldArray = copyArray(array);
|
|
@@ -3706,10 +3706,10 @@ var require_lodash = __commonJS({
|
|
|
3706
3706
|
}
|
|
3707
3707
|
return isString(collection) ? fromIndex <= length && collection.indexOf(value, fromIndex) > -1 : !!length && baseIndexOf(collection, value, fromIndex) > -1;
|
|
3708
3708
|
}
|
|
3709
|
-
var invokeMap = baseRest(function(collection,
|
|
3710
|
-
var index = -1, isFunc = typeof
|
|
3709
|
+
var invokeMap = baseRest(function(collection, path4, args) {
|
|
3710
|
+
var index = -1, isFunc = typeof path4 == "function", result2 = isArrayLike(collection) ? Array2(collection.length) : [];
|
|
3711
3711
|
baseEach(collection, function(value) {
|
|
3712
|
-
result2[++index] = isFunc ? apply(
|
|
3712
|
+
result2[++index] = isFunc ? apply(path4, value, args) : baseInvoke(value, path4, args);
|
|
3713
3713
|
});
|
|
3714
3714
|
return result2;
|
|
3715
3715
|
});
|
|
@@ -4361,15 +4361,15 @@ var require_lodash = __commonJS({
|
|
|
4361
4361
|
function functionsIn(object) {
|
|
4362
4362
|
return object == null ? [] : baseFunctions(object, keysIn(object));
|
|
4363
4363
|
}
|
|
4364
|
-
function get(object,
|
|
4365
|
-
var result2 = object == null ? undefined2 : baseGet(object,
|
|
4364
|
+
function get(object, path4, defaultValue) {
|
|
4365
|
+
var result2 = object == null ? undefined2 : baseGet(object, path4);
|
|
4366
4366
|
return result2 === undefined2 ? defaultValue : result2;
|
|
4367
4367
|
}
|
|
4368
|
-
function has(object,
|
|
4369
|
-
return object != null && hasPath(object,
|
|
4368
|
+
function has(object, path4) {
|
|
4369
|
+
return object != null && hasPath(object, path4, baseHas);
|
|
4370
4370
|
}
|
|
4371
|
-
function hasIn(object,
|
|
4372
|
-
return object != null && hasPath(object,
|
|
4371
|
+
function hasIn(object, path4) {
|
|
4372
|
+
return object != null && hasPath(object, path4, baseHasIn);
|
|
4373
4373
|
}
|
|
4374
4374
|
var invert = createInverter(function(result2, value, key) {
|
|
4375
4375
|
if (value != null && typeof value.toString != "function") {
|
|
@@ -4422,10 +4422,10 @@ var require_lodash = __commonJS({
|
|
|
4422
4422
|
return result2;
|
|
4423
4423
|
}
|
|
4424
4424
|
var isDeep = false;
|
|
4425
|
-
paths = arrayMap(paths, function(
|
|
4426
|
-
|
|
4427
|
-
isDeep || (isDeep =
|
|
4428
|
-
return
|
|
4425
|
+
paths = arrayMap(paths, function(path4) {
|
|
4426
|
+
path4 = castPath(path4, object);
|
|
4427
|
+
isDeep || (isDeep = path4.length > 1);
|
|
4428
|
+
return path4;
|
|
4429
4429
|
});
|
|
4430
4430
|
copyObject(object, getAllKeysIn(object), result2);
|
|
4431
4431
|
if (isDeep) {
|
|
@@ -4451,19 +4451,19 @@ var require_lodash = __commonJS({
|
|
|
4451
4451
|
return [prop];
|
|
4452
4452
|
});
|
|
4453
4453
|
predicate = getIteratee(predicate);
|
|
4454
|
-
return basePickBy(object, props, function(value,
|
|
4455
|
-
return predicate(value,
|
|
4454
|
+
return basePickBy(object, props, function(value, path4) {
|
|
4455
|
+
return predicate(value, path4[0]);
|
|
4456
4456
|
});
|
|
4457
4457
|
}
|
|
4458
|
-
function result(object,
|
|
4459
|
-
|
|
4460
|
-
var index = -1, length =
|
|
4458
|
+
function result(object, path4, defaultValue) {
|
|
4459
|
+
path4 = castPath(path4, object);
|
|
4460
|
+
var index = -1, length = path4.length;
|
|
4461
4461
|
if (!length) {
|
|
4462
4462
|
length = 1;
|
|
4463
4463
|
object = undefined2;
|
|
4464
4464
|
}
|
|
4465
4465
|
while (++index < length) {
|
|
4466
|
-
var value = object == null ? undefined2 : object[toKey(
|
|
4466
|
+
var value = object == null ? undefined2 : object[toKey(path4[index])];
|
|
4467
4467
|
if (value === undefined2) {
|
|
4468
4468
|
index = length;
|
|
4469
4469
|
value = defaultValue;
|
|
@@ -4472,12 +4472,12 @@ var require_lodash = __commonJS({
|
|
|
4472
4472
|
}
|
|
4473
4473
|
return object;
|
|
4474
4474
|
}
|
|
4475
|
-
function set(object,
|
|
4476
|
-
return object == null ? object : baseSet(object,
|
|
4475
|
+
function set(object, path4, value) {
|
|
4476
|
+
return object == null ? object : baseSet(object, path4, value);
|
|
4477
4477
|
}
|
|
4478
|
-
function setWith(object,
|
|
4478
|
+
function setWith(object, path4, value, customizer) {
|
|
4479
4479
|
customizer = typeof customizer == "function" ? customizer : undefined2;
|
|
4480
|
-
return object == null ? object : baseSet(object,
|
|
4480
|
+
return object == null ? object : baseSet(object, path4, value, customizer);
|
|
4481
4481
|
}
|
|
4482
4482
|
var toPairs = createToPairs(keys);
|
|
4483
4483
|
var toPairsIn = createToPairs(keysIn);
|
|
@@ -4499,15 +4499,15 @@ var require_lodash = __commonJS({
|
|
|
4499
4499
|
});
|
|
4500
4500
|
return accumulator;
|
|
4501
4501
|
}
|
|
4502
|
-
function unset(object,
|
|
4503
|
-
return object == null ? true : baseUnset(object,
|
|
4502
|
+
function unset(object, path4) {
|
|
4503
|
+
return object == null ? true : baseUnset(object, path4);
|
|
4504
4504
|
}
|
|
4505
|
-
function update2(object,
|
|
4506
|
-
return object == null ? object : baseUpdate(object,
|
|
4505
|
+
function update2(object, path4, updater) {
|
|
4506
|
+
return object == null ? object : baseUpdate(object, path4, castFunction(updater));
|
|
4507
4507
|
}
|
|
4508
|
-
function updateWith(object,
|
|
4508
|
+
function updateWith(object, path4, updater, customizer) {
|
|
4509
4509
|
customizer = typeof customizer == "function" ? customizer : undefined2;
|
|
4510
|
-
return object == null ? object : baseUpdate(object,
|
|
4510
|
+
return object == null ? object : baseUpdate(object, path4, castFunction(updater), customizer);
|
|
4511
4511
|
}
|
|
4512
4512
|
function values(object) {
|
|
4513
4513
|
return object == null ? [] : baseValues(object, keys(object));
|
|
@@ -4893,17 +4893,17 @@ var require_lodash = __commonJS({
|
|
|
4893
4893
|
function matches(source) {
|
|
4894
4894
|
return baseMatches(baseClone(source, CLONE_DEEP_FLAG));
|
|
4895
4895
|
}
|
|
4896
|
-
function matchesProperty(
|
|
4897
|
-
return baseMatchesProperty(
|
|
4896
|
+
function matchesProperty(path4, srcValue) {
|
|
4897
|
+
return baseMatchesProperty(path4, baseClone(srcValue, CLONE_DEEP_FLAG));
|
|
4898
4898
|
}
|
|
4899
|
-
var method = baseRest(function(
|
|
4899
|
+
var method = baseRest(function(path4, args) {
|
|
4900
4900
|
return function(object) {
|
|
4901
|
-
return baseInvoke(object,
|
|
4901
|
+
return baseInvoke(object, path4, args);
|
|
4902
4902
|
};
|
|
4903
4903
|
});
|
|
4904
4904
|
var methodOf = baseRest(function(object, args) {
|
|
4905
|
-
return function(
|
|
4906
|
-
return baseInvoke(object,
|
|
4905
|
+
return function(path4) {
|
|
4906
|
+
return baseInvoke(object, path4, args);
|
|
4907
4907
|
};
|
|
4908
4908
|
});
|
|
4909
4909
|
function mixin(object, source, options) {
|
|
@@ -4950,12 +4950,12 @@ var require_lodash = __commonJS({
|
|
|
4950
4950
|
var over = createOver(arrayMap);
|
|
4951
4951
|
var overEvery = createOver(arrayEvery);
|
|
4952
4952
|
var overSome = createOver(arraySome);
|
|
4953
|
-
function property(
|
|
4954
|
-
return isKey(
|
|
4953
|
+
function property(path4) {
|
|
4954
|
+
return isKey(path4) ? baseProperty(toKey(path4)) : basePropertyDeep(path4);
|
|
4955
4955
|
}
|
|
4956
4956
|
function propertyOf(object) {
|
|
4957
|
-
return function(
|
|
4958
|
-
return object == null ? undefined2 : baseGet(object,
|
|
4957
|
+
return function(path4) {
|
|
4958
|
+
return object == null ? undefined2 : baseGet(object, path4);
|
|
4959
4959
|
};
|
|
4960
4960
|
}
|
|
4961
4961
|
var range = createRange();
|
|
@@ -5408,12 +5408,12 @@ var require_lodash = __commonJS({
|
|
|
5408
5408
|
LazyWrapper.prototype.findLast = function(predicate) {
|
|
5409
5409
|
return this.reverse().find(predicate);
|
|
5410
5410
|
};
|
|
5411
|
-
LazyWrapper.prototype.invokeMap = baseRest(function(
|
|
5412
|
-
if (typeof
|
|
5411
|
+
LazyWrapper.prototype.invokeMap = baseRest(function(path4, args) {
|
|
5412
|
+
if (typeof path4 == "function") {
|
|
5413
5413
|
return new LazyWrapper(this);
|
|
5414
5414
|
}
|
|
5415
5415
|
return this.map(function(value) {
|
|
5416
|
-
return baseInvoke(value,
|
|
5416
|
+
return baseInvoke(value, path4, args);
|
|
5417
5417
|
});
|
|
5418
5418
|
});
|
|
5419
5419
|
LazyWrapper.prototype.reject = function(predicate) {
|
|
@@ -5535,9 +5535,12 @@ __export(cli_exports, {
|
|
|
5535
5535
|
cleanFiles: () => cleanFiles,
|
|
5536
5536
|
cleanRedis: () => cleanRedis,
|
|
5537
5537
|
drawStatInCLI: () => drawStatInCLI,
|
|
5538
|
+
formatRuntimeCloseNotification: () => formatRuntimeCloseNotification,
|
|
5538
5539
|
getTickers: () => getTickers,
|
|
5539
5540
|
loadTradejsConfig: () => loadTradejsConfig,
|
|
5540
5541
|
makeScreenshots: () => makeScreenshots,
|
|
5542
|
+
sendDocumentToTG: () => sendDocumentToTG,
|
|
5543
|
+
sendRuntimeCloseNotificationsToTG: () => sendRuntimeCloseNotificationsToTG,
|
|
5541
5544
|
sendTextToTG: () => sendTextToTG,
|
|
5542
5545
|
sendToAI: () => sendToAI,
|
|
5543
5546
|
sendToTG: () => sendToTG,
|
|
@@ -5545,6 +5548,7 @@ __export(cli_exports, {
|
|
|
5545
5548
|
});
|
|
5546
5549
|
module.exports = __toCommonJS(cli_exports);
|
|
5547
5550
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
5551
|
+
var import_path3 = __toESM(require("path"));
|
|
5548
5552
|
var import_lodash = __toESM(require_lodash());
|
|
5549
5553
|
var import_progress = __toESM(require("progress"));
|
|
5550
5554
|
var import_chalk = __toESM(require("chalk"));
|
|
@@ -5573,6 +5577,7 @@ var SCREENSHOT_CONCURRENCY_LIMIT = NODE_ENV === "production" ? 1 : 2;
|
|
|
5573
5577
|
var import_files = require("@tradejs/infra/files");
|
|
5574
5578
|
var import_redis3 = require("@tradejs/infra/redis");
|
|
5575
5579
|
var import_logger5 = require("@tradejs/infra/logger");
|
|
5580
|
+
var import_timescale = require("@tradejs/infra/timescale");
|
|
5576
5581
|
|
|
5577
5582
|
// src/ai.ts
|
|
5578
5583
|
var import_aiLanguages = require("@tradejs/infra/aiLanguages");
|
|
@@ -5581,6 +5586,10 @@ var import_userSettings = require("@tradejs/infra/userSettings");
|
|
|
5581
5586
|
|
|
5582
5587
|
// src/aiShared.ts
|
|
5583
5588
|
var MAX_AI_SERIES_POINTS = 5;
|
|
5589
|
+
var COMPACT_INDICATORS_SNAPSHOT_SYMBOL = /* @__PURE__ */ Symbol.for(
|
|
5590
|
+
"tradejs.indicators.compactSnapshot"
|
|
5591
|
+
);
|
|
5592
|
+
var COMPACT_INDICATORS_SNAPSHOT_KEY = "__tradejsCompactIndicatorsSnapshot";
|
|
5584
5593
|
var trimSeriesDeep = (value) => {
|
|
5585
5594
|
if (Array.isArray(value)) {
|
|
5586
5595
|
const trimmed = value.slice(-MAX_AI_SERIES_POINTS);
|
|
@@ -5602,13 +5611,15 @@ var trimSeriesDeep = (value) => {
|
|
|
5602
5611
|
}
|
|
5603
5612
|
return value;
|
|
5604
5613
|
};
|
|
5614
|
+
var buildCompactAiIndicatorsSnapshot = (value) => {
|
|
5615
|
+
const compactSnapshot = value && typeof value === "object" ? value[COMPACT_INDICATORS_SNAPSHOT_SYMBOL] ?? value[COMPACT_INDICATORS_SNAPSHOT_KEY] : void 0;
|
|
5616
|
+
if (typeof compactSnapshot === "function") {
|
|
5617
|
+
return compactSnapshot({ limit: MAX_AI_SERIES_POINTS });
|
|
5618
|
+
}
|
|
5619
|
+
return trimSeriesDeep(value);
|
|
5620
|
+
};
|
|
5605
5621
|
|
|
5606
5622
|
// src/aiMarketContext.ts
|
|
5607
|
-
var SESSION_WINDOWS = [
|
|
5608
|
-
{ name: "asia", startMinuteUtc: 0, endMinuteUtc: 8 * 60 },
|
|
5609
|
-
{ name: "europe", startMinuteUtc: 7 * 60, endMinuteUtc: 16 * 60 },
|
|
5610
|
-
{ name: "us", startMinuteUtc: 13 * 60, endMinuteUtc: 22 * 60 }
|
|
5611
|
-
];
|
|
5612
5623
|
var toRecord = (value) => {
|
|
5613
5624
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
5614
5625
|
return null;
|
|
@@ -5619,92 +5630,485 @@ var toFiniteNumber = (value) => {
|
|
|
5619
5630
|
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
5620
5631
|
return Number.isFinite(numeric) ? numeric : null;
|
|
5621
5632
|
};
|
|
5622
|
-
var getLastFiniteNumber = (value) => {
|
|
5623
|
-
const numeric = toFiniteNumber(value);
|
|
5624
|
-
if (numeric != null) {
|
|
5625
|
-
return numeric;
|
|
5626
|
-
}
|
|
5627
|
-
if (!Array.isArray(value)) {
|
|
5628
|
-
return null;
|
|
5629
|
-
}
|
|
5630
|
-
for (let i = value.length - 1; i >= 0; i -= 1) {
|
|
5631
|
-
const nested = getLastFiniteNumber(value[i]);
|
|
5632
|
-
if (nested != null) {
|
|
5633
|
-
return nested;
|
|
5634
|
-
}
|
|
5635
|
-
}
|
|
5636
|
-
return null;
|
|
5637
|
-
};
|
|
5638
5633
|
var roundTo = (value, decimals) => {
|
|
5639
5634
|
const factor = 10 ** decimals;
|
|
5640
5635
|
return Math.round(value * factor) / factor;
|
|
5641
5636
|
};
|
|
5642
|
-
var isInsideSession = (minuteUtc, startMinuteUtc, endMinuteUtc) => startMinuteUtc <= endMinuteUtc ? minuteUtc >= startMinuteUtc && minuteUtc < endMinuteUtc : minuteUtc >= startMinuteUtc || minuteUtc < endMinuteUtc;
|
|
5643
|
-
var buildTradingSessionContext = (timestamp) => {
|
|
5644
|
-
const date = new Date(timestamp);
|
|
5645
|
-
const utcHour = date.getUTCHours();
|
|
5646
|
-
const utcMinute = date.getUTCMinutes();
|
|
5647
|
-
const minuteUtc = utcHour * 60 + utcMinute;
|
|
5648
|
-
const activeSessions = SESSION_WINDOWS.filter(
|
|
5649
|
-
(session) => isInsideSession(minuteUtc, session.startMinuteUtc, session.endMinuteUtc)
|
|
5650
|
-
).map((session) => session.name);
|
|
5651
|
-
const primarySession = activeSessions.includes("us") ? "us" : activeSessions.includes("europe") ? "europe" : activeSessions.includes("asia") ? "asia" : "off_hours";
|
|
5652
|
-
return {
|
|
5653
|
-
timezone: "UTC",
|
|
5654
|
-
utcHour,
|
|
5655
|
-
utcMinute,
|
|
5656
|
-
primarySession,
|
|
5657
|
-
activeSessions,
|
|
5658
|
-
isOverlap: activeSessions.length > 1,
|
|
5659
|
-
overlap: activeSessions.length > 1 ? `${activeSessions.join("_")}_overlap` : null
|
|
5660
|
-
};
|
|
5661
|
-
};
|
|
5662
5637
|
var buildMissingSpreadContext = () => ({
|
|
5663
|
-
source: "
|
|
5664
|
-
indicatorKey: "payload.
|
|
5638
|
+
source: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
|
|
5639
|
+
indicatorKey: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
|
|
5665
5640
|
available: false,
|
|
5666
5641
|
value: null,
|
|
5642
|
+
zScore: null,
|
|
5667
5643
|
bps: null,
|
|
5668
5644
|
absBps: null,
|
|
5669
5645
|
bias: null,
|
|
5670
5646
|
severity: null
|
|
5671
5647
|
});
|
|
5672
|
-
var
|
|
5648
|
+
var buildSpreadContextFromSignal = (signal) => {
|
|
5649
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
5650
|
+
const relative = toRecord(baseContext?.relative);
|
|
5651
|
+
const execution = toRecord(relative?.execution);
|
|
5652
|
+
const spread = toFiniteNumber(execution?.venueSpread);
|
|
5653
|
+
const zScore = toFiniteNumber(execution?.venueSpreadZScore);
|
|
5654
|
+
if (spread == null) {
|
|
5655
|
+
return buildMissingSpreadContext();
|
|
5656
|
+
}
|
|
5673
5657
|
const value = roundTo(spread, 8);
|
|
5674
5658
|
const bps = roundTo(value * 1e4, 2);
|
|
5675
5659
|
const absBps = Math.abs(bps);
|
|
5676
|
-
const bias =
|
|
5660
|
+
const bias = absBps < 1 ? "flat" : bps > 0 ? "coinbase_premium" : "binance_premium";
|
|
5677
5661
|
const severity = absBps >= 20 ? "wide" : absBps >= 5 ? "elevated" : "normal";
|
|
5678
5662
|
return {
|
|
5679
|
-
source: "
|
|
5680
|
-
indicatorKey: "payload.
|
|
5663
|
+
source: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
|
|
5664
|
+
indicatorKey: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
|
|
5681
5665
|
available: true,
|
|
5682
5666
|
value,
|
|
5667
|
+
zScore,
|
|
5683
5668
|
bps,
|
|
5684
5669
|
absBps,
|
|
5685
5670
|
bias,
|
|
5686
5671
|
severity
|
|
5687
5672
|
};
|
|
5688
5673
|
};
|
|
5689
|
-
var
|
|
5690
|
-
const
|
|
5691
|
-
|
|
5692
|
-
|
|
5674
|
+
var buildTrueDeltaContextFromSignal = (signal) => {
|
|
5675
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
5676
|
+
const participation = toRecord(baseContext?.participation);
|
|
5677
|
+
const delta = toRecord(participation?.delta);
|
|
5678
|
+
const source = String(delta?.source ?? "");
|
|
5679
|
+
const isTrueDeltaSource = source === "kline_taker_volume" || source === "agg_trades" || source === "trades";
|
|
5680
|
+
if (!delta || !isTrueDeltaSource) {
|
|
5681
|
+
return {
|
|
5682
|
+
source: source || null,
|
|
5683
|
+
available: false,
|
|
5684
|
+
buyPressurePct: null,
|
|
5685
|
+
buyVolume: null,
|
|
5686
|
+
sellVolume: null,
|
|
5687
|
+
netDelta: null,
|
|
5688
|
+
deltaPct: null,
|
|
5689
|
+
signedVolumeZScore: null
|
|
5690
|
+
};
|
|
5693
5691
|
}
|
|
5694
|
-
return
|
|
5692
|
+
return {
|
|
5693
|
+
source,
|
|
5694
|
+
available: true,
|
|
5695
|
+
buyPressurePct: toFiniteNumber(delta.buyPressurePct),
|
|
5696
|
+
buyVolume: toFiniteNumber(delta.buyVolume),
|
|
5697
|
+
sellVolume: toFiniteNumber(delta.sellVolume),
|
|
5698
|
+
netDelta: toFiniteNumber(delta.netDelta),
|
|
5699
|
+
deltaPct: toFiniteNumber(delta.deltaPct),
|
|
5700
|
+
signedVolumeZScore: toFiniteNumber(delta.signedVolumeZScore)
|
|
5701
|
+
};
|
|
5695
5702
|
};
|
|
5696
|
-
var
|
|
5697
|
-
const
|
|
5698
|
-
|
|
5699
|
-
);
|
|
5700
|
-
|
|
5701
|
-
|
|
5703
|
+
var buildTradeFlowContextFromSignal = (signal) => {
|
|
5704
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
5705
|
+
const participation = toRecord(baseContext?.participation);
|
|
5706
|
+
const tradeFlow = toRecord(participation?.tradeFlow);
|
|
5707
|
+
if (!tradeFlow) {
|
|
5708
|
+
return {
|
|
5709
|
+
source: null,
|
|
5710
|
+
available: false,
|
|
5711
|
+
interval: null,
|
|
5712
|
+
stale: null,
|
|
5713
|
+
trades: null,
|
|
5714
|
+
buyPressurePct: null,
|
|
5715
|
+
netBaseDelta: null,
|
|
5716
|
+
netQuoteDelta: null
|
|
5717
|
+
};
|
|
5718
|
+
}
|
|
5719
|
+
return {
|
|
5720
|
+
source: String(tradeFlow.source ?? ""),
|
|
5721
|
+
available: true,
|
|
5722
|
+
interval: String(tradeFlow.interval ?? ""),
|
|
5723
|
+
stale: typeof tradeFlow.stale === "boolean" ? tradeFlow.stale : null,
|
|
5724
|
+
trades: toFiniteNumber(tradeFlow.trades),
|
|
5725
|
+
buyPressurePct: toFiniteNumber(tradeFlow.buyPressurePct),
|
|
5726
|
+
netBaseDelta: toFiniteNumber(tradeFlow.netBaseDelta),
|
|
5727
|
+
netQuoteDelta: toFiniteNumber(tradeFlow.netQuoteDelta)
|
|
5728
|
+
};
|
|
5729
|
+
};
|
|
5730
|
+
var buildMarketBreadthContextFromSignal = (signal) => {
|
|
5731
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
5732
|
+
const relative = toRecord(baseContext?.relative);
|
|
5733
|
+
const breadth = toRecord(relative?.marketBreadth);
|
|
5734
|
+
if (!breadth) {
|
|
5735
|
+
return {
|
|
5736
|
+
source: null,
|
|
5737
|
+
available: false,
|
|
5738
|
+
universe: null,
|
|
5739
|
+
interval: null,
|
|
5740
|
+
stale: null,
|
|
5741
|
+
symbolsCount: null,
|
|
5742
|
+
advanceDeclineRatio: null,
|
|
5743
|
+
pctAboveMa20: null,
|
|
5744
|
+
pctAboveMa50: null,
|
|
5745
|
+
equalWeightedReturn: null,
|
|
5746
|
+
volumeWeightedReturn: null,
|
|
5747
|
+
dispersion: null
|
|
5748
|
+
};
|
|
5749
|
+
}
|
|
5750
|
+
return {
|
|
5751
|
+
source: String(breadth.source ?? ""),
|
|
5752
|
+
available: true,
|
|
5753
|
+
universe: String(breadth.universe ?? ""),
|
|
5754
|
+
interval: String(breadth.interval ?? ""),
|
|
5755
|
+
stale: typeof breadth.stale === "boolean" ? breadth.stale : null,
|
|
5756
|
+
symbolsCount: toFiniteNumber(breadth.symbolsCount),
|
|
5757
|
+
advanceDeclineRatio: toFiniteNumber(breadth.advanceDeclineRatio),
|
|
5758
|
+
pctAboveMa20: toFiniteNumber(breadth.pctAboveMa20),
|
|
5759
|
+
pctAboveMa50: toFiniteNumber(breadth.pctAboveMa50),
|
|
5760
|
+
equalWeightedReturn: toFiniteNumber(breadth.equalWeightedReturn),
|
|
5761
|
+
volumeWeightedReturn: toFiniteNumber(breadth.volumeWeightedReturn),
|
|
5762
|
+
dispersion: toFiniteNumber(breadth.dispersion)
|
|
5763
|
+
};
|
|
5764
|
+
};
|
|
5765
|
+
var buildTargetVsBtcContextFromSignal = (signal) => {
|
|
5766
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
5767
|
+
const relative = toRecord(baseContext?.relative);
|
|
5768
|
+
const targetVsBtc = toRecord(relative?.targetVsBtc);
|
|
5769
|
+
if (!targetVsBtc) {
|
|
5770
|
+
return {
|
|
5771
|
+
source: null,
|
|
5772
|
+
available: false,
|
|
5773
|
+
ratioReturn1h: null,
|
|
5774
|
+
ratioReturn4h: null,
|
|
5775
|
+
ratioReturn24h: null,
|
|
5776
|
+
alphaVsBtc1h: null,
|
|
5777
|
+
alphaVsBtc4h: null,
|
|
5778
|
+
alphaVsBtc24h: null,
|
|
5779
|
+
betaToBtc20: null,
|
|
5780
|
+
correlationToBtc20: null,
|
|
5781
|
+
ratioTrend: null
|
|
5782
|
+
};
|
|
5783
|
+
}
|
|
5784
|
+
return {
|
|
5785
|
+
source: String(targetVsBtc.source ?? ""),
|
|
5786
|
+
available: true,
|
|
5787
|
+
ratioReturn1h: toFiniteNumber(targetVsBtc.ratioReturn1h),
|
|
5788
|
+
ratioReturn4h: toFiniteNumber(targetVsBtc.ratioReturn4h),
|
|
5789
|
+
ratioReturn24h: toFiniteNumber(targetVsBtc.ratioReturn24h),
|
|
5790
|
+
alphaVsBtc1h: toFiniteNumber(targetVsBtc.alphaVsBtc1h),
|
|
5791
|
+
alphaVsBtc4h: toFiniteNumber(targetVsBtc.alphaVsBtc4h),
|
|
5792
|
+
alphaVsBtc24h: toFiniteNumber(targetVsBtc.alphaVsBtc24h),
|
|
5793
|
+
betaToBtc20: toFiniteNumber(targetVsBtc.betaToBtc20),
|
|
5794
|
+
correlationToBtc20: toFiniteNumber(targetVsBtc.correlationToBtc20),
|
|
5795
|
+
ratioTrend: typeof targetVsBtc.ratioTrend === "string" ? targetVsBtc.ratioTrend : null
|
|
5796
|
+
};
|
|
5797
|
+
};
|
|
5798
|
+
var buildBtcAltRegimeContextFromSignal = (signal) => {
|
|
5799
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
5800
|
+
const relative = toRecord(baseContext?.relative);
|
|
5801
|
+
const btcAltRegime = toRecord(relative?.btcAltRegime);
|
|
5802
|
+
if (!btcAltRegime) {
|
|
5803
|
+
return {
|
|
5804
|
+
source: null,
|
|
5805
|
+
available: false,
|
|
5806
|
+
universe: null,
|
|
5807
|
+
interval: null,
|
|
5808
|
+
stale: null,
|
|
5809
|
+
regime: null,
|
|
5810
|
+
btcReturn24h: null,
|
|
5811
|
+
altBasketReturn24h: null,
|
|
5812
|
+
btcVsAltReturn24h: null,
|
|
5813
|
+
btcTurnoverShare24h: null,
|
|
5814
|
+
btcTurnoverShareChange24h: null,
|
|
5815
|
+
altVolToBtcVol24h: null,
|
|
5816
|
+
altDispersion24h: null
|
|
5817
|
+
};
|
|
5818
|
+
}
|
|
5819
|
+
return {
|
|
5820
|
+
source: String(btcAltRegime.source ?? ""),
|
|
5821
|
+
available: true,
|
|
5822
|
+
universe: String(btcAltRegime.universe ?? ""),
|
|
5823
|
+
interval: String(btcAltRegime.interval ?? ""),
|
|
5824
|
+
stale: typeof btcAltRegime.stale === "boolean" ? btcAltRegime.stale : null,
|
|
5825
|
+
regime: typeof btcAltRegime.regime === "string" ? btcAltRegime.regime : null,
|
|
5826
|
+
btcReturn24h: toFiniteNumber(btcAltRegime.btcReturn24h),
|
|
5827
|
+
altBasketReturn24h: toFiniteNumber(btcAltRegime.altBasketReturn24h),
|
|
5828
|
+
btcVsAltReturn24h: toFiniteNumber(btcAltRegime.btcVsAltReturn24h),
|
|
5829
|
+
btcTurnoverShare24h: toFiniteNumber(btcAltRegime.btcTurnoverShare24h),
|
|
5830
|
+
btcTurnoverShareChange24h: toFiniteNumber(
|
|
5831
|
+
btcAltRegime.btcTurnoverShareChange24h
|
|
5832
|
+
),
|
|
5833
|
+
altVolToBtcVol24h: toFiniteNumber(btcAltRegime.altVolToBtcVol24h),
|
|
5834
|
+
altDispersion24h: toFiniteNumber(btcAltRegime.altDispersion24h)
|
|
5835
|
+
};
|
|
5836
|
+
};
|
|
5837
|
+
var buildCmcGlobalContextFromSignal = (signal) => {
|
|
5838
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
5839
|
+
const relative = toRecord(baseContext?.relative);
|
|
5840
|
+
const cmcGlobal = toRecord(relative?.cmcGlobal);
|
|
5841
|
+
if (!cmcGlobal) {
|
|
5842
|
+
return {
|
|
5843
|
+
source: null,
|
|
5844
|
+
available: false,
|
|
5845
|
+
interval: null,
|
|
5846
|
+
asOfTs: null,
|
|
5847
|
+
stale: null,
|
|
5848
|
+
totalMarketCapUsd: null,
|
|
5849
|
+
totalVolumeUsd: null,
|
|
5850
|
+
totalVolumeReportedUsd: null,
|
|
5851
|
+
altMarketCapUsd: null,
|
|
5852
|
+
altVolumeUsd: null,
|
|
5853
|
+
altVolumeReportedUsd: null,
|
|
5854
|
+
btcDominancePct: null,
|
|
5855
|
+
ethDominancePct: null,
|
|
5856
|
+
btcDominanceChange24hPct: null,
|
|
5857
|
+
ethDominanceChange24hPct: null,
|
|
5858
|
+
altMarketCapChange24hPct: null,
|
|
5859
|
+
altVolumeChange24hPct: null,
|
|
5860
|
+
activeCryptocurrencies: null,
|
|
5861
|
+
activeExchanges: null,
|
|
5862
|
+
activeMarketPairs: null,
|
|
5863
|
+
altLiquidityRegime: null
|
|
5864
|
+
};
|
|
5865
|
+
}
|
|
5866
|
+
return {
|
|
5867
|
+
source: String(cmcGlobal.source ?? ""),
|
|
5868
|
+
available: true,
|
|
5869
|
+
interval: typeof cmcGlobal.interval === "string" ? cmcGlobal.interval : null,
|
|
5870
|
+
asOfTs: toFiniteNumber(cmcGlobal.asOfTs),
|
|
5871
|
+
stale: typeof cmcGlobal.stale === "boolean" ? cmcGlobal.stale : null,
|
|
5872
|
+
totalMarketCapUsd: toFiniteNumber(cmcGlobal.totalMarketCapUsd),
|
|
5873
|
+
totalVolumeUsd: toFiniteNumber(cmcGlobal.totalVolumeUsd),
|
|
5874
|
+
totalVolumeReportedUsd: toFiniteNumber(cmcGlobal.totalVolumeReportedUsd),
|
|
5875
|
+
altMarketCapUsd: toFiniteNumber(cmcGlobal.altMarketCapUsd),
|
|
5876
|
+
altVolumeUsd: toFiniteNumber(cmcGlobal.altVolumeUsd),
|
|
5877
|
+
altVolumeReportedUsd: toFiniteNumber(cmcGlobal.altVolumeReportedUsd),
|
|
5878
|
+
btcDominancePct: toFiniteNumber(cmcGlobal.btcDominancePct),
|
|
5879
|
+
ethDominancePct: toFiniteNumber(cmcGlobal.ethDominancePct),
|
|
5880
|
+
btcDominanceChange24hPct: toFiniteNumber(
|
|
5881
|
+
cmcGlobal.btcDominanceChange24hPct
|
|
5882
|
+
),
|
|
5883
|
+
ethDominanceChange24hPct: toFiniteNumber(
|
|
5884
|
+
cmcGlobal.ethDominanceChange24hPct
|
|
5885
|
+
),
|
|
5886
|
+
altMarketCapChange24hPct: toFiniteNumber(
|
|
5887
|
+
cmcGlobal.altMarketCapChange24hPct
|
|
5888
|
+
),
|
|
5889
|
+
altVolumeChange24hPct: toFiniteNumber(cmcGlobal.altVolumeChange24hPct),
|
|
5890
|
+
activeCryptocurrencies: toFiniteNumber(cmcGlobal.activeCryptocurrencies),
|
|
5891
|
+
activeExchanges: toFiniteNumber(cmcGlobal.activeExchanges),
|
|
5892
|
+
activeMarketPairs: toFiniteNumber(cmcGlobal.activeMarketPairs),
|
|
5893
|
+
altLiquidityRegime: typeof cmcGlobal.altLiquidityRegime === "string" ? cmcGlobal.altLiquidityRegime : null
|
|
5894
|
+
};
|
|
5895
|
+
};
|
|
5896
|
+
var buildCmcReferenceAssetsContextFromSignal = (signal) => {
|
|
5897
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
5898
|
+
const relative = toRecord(baseContext?.relative);
|
|
5899
|
+
const cmcReferenceAssets = toRecord(relative?.cmcReferenceAssets);
|
|
5900
|
+
if (!cmcReferenceAssets) {
|
|
5901
|
+
return {
|
|
5902
|
+
source: null,
|
|
5903
|
+
available: false,
|
|
5904
|
+
interval: null,
|
|
5905
|
+
asOfTs: null,
|
|
5906
|
+
stale: null,
|
|
5907
|
+
btcMarketCapUsd: null,
|
|
5908
|
+
ethMarketCapUsd: null,
|
|
5909
|
+
btcVolumeUsd: null,
|
|
5910
|
+
ethVolumeUsd: null,
|
|
5911
|
+
btcVolumeToMarketCap: null,
|
|
5912
|
+
ethVolumeToMarketCap: null,
|
|
5913
|
+
ethBtcMarketCapRatio: null,
|
|
5914
|
+
ethBtcMarketCapRatioChange24hPct: null,
|
|
5915
|
+
ethVsBtcVolumeRatio: null,
|
|
5916
|
+
referenceLiquidityRegime: null
|
|
5917
|
+
};
|
|
5918
|
+
}
|
|
5919
|
+
return {
|
|
5920
|
+
source: String(cmcReferenceAssets.source ?? ""),
|
|
5921
|
+
available: true,
|
|
5922
|
+
interval: typeof cmcReferenceAssets.interval === "string" ? cmcReferenceAssets.interval : null,
|
|
5923
|
+
asOfTs: toFiniteNumber(cmcReferenceAssets.asOfTs),
|
|
5924
|
+
stale: typeof cmcReferenceAssets.stale === "boolean" ? cmcReferenceAssets.stale : null,
|
|
5925
|
+
btcMarketCapUsd: toFiniteNumber(cmcReferenceAssets.btcMarketCapUsd),
|
|
5926
|
+
ethMarketCapUsd: toFiniteNumber(cmcReferenceAssets.ethMarketCapUsd),
|
|
5927
|
+
btcVolumeUsd: toFiniteNumber(cmcReferenceAssets.btcVolumeUsd),
|
|
5928
|
+
ethVolumeUsd: toFiniteNumber(cmcReferenceAssets.ethVolumeUsd),
|
|
5929
|
+
btcVolumeToMarketCap: toFiniteNumber(
|
|
5930
|
+
cmcReferenceAssets.btcVolumeToMarketCap
|
|
5931
|
+
),
|
|
5932
|
+
ethVolumeToMarketCap: toFiniteNumber(
|
|
5933
|
+
cmcReferenceAssets.ethVolumeToMarketCap
|
|
5934
|
+
),
|
|
5935
|
+
ethBtcMarketCapRatio: toFiniteNumber(
|
|
5936
|
+
cmcReferenceAssets.ethBtcMarketCapRatio
|
|
5937
|
+
),
|
|
5938
|
+
ethBtcMarketCapRatioChange24hPct: toFiniteNumber(
|
|
5939
|
+
cmcReferenceAssets.ethBtcMarketCapRatioChange24hPct
|
|
5940
|
+
),
|
|
5941
|
+
ethVsBtcVolumeRatio: toFiniteNumber(cmcReferenceAssets.ethVsBtcVolumeRatio),
|
|
5942
|
+
referenceLiquidityRegime: typeof cmcReferenceAssets.referenceLiquidityRegime === "string" ? cmcReferenceAssets.referenceLiquidityRegime : null
|
|
5943
|
+
};
|
|
5944
|
+
};
|
|
5945
|
+
var buildCmcExchangeLiquidityContextFromSignal = (signal) => {
|
|
5946
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
5947
|
+
const relative = toRecord(baseContext?.relative);
|
|
5948
|
+
const cmcExchangeLiquidity = toRecord(relative?.cmcExchangeLiquidity);
|
|
5949
|
+
if (!cmcExchangeLiquidity) {
|
|
5950
|
+
return {
|
|
5951
|
+
source: null,
|
|
5952
|
+
available: false,
|
|
5953
|
+
interval: null,
|
|
5954
|
+
asOfTs: null,
|
|
5955
|
+
stale: null,
|
|
5956
|
+
exchangesCount: null,
|
|
5957
|
+
totalVolumeUsd: null,
|
|
5958
|
+
totalVolumeChange24hPct: null,
|
|
5959
|
+
binanceVolumeUsd: null,
|
|
5960
|
+
binanceVolumeShare: null,
|
|
5961
|
+
topExchangeVolumeShare: null,
|
|
5962
|
+
liquidityRegime: null
|
|
5963
|
+
};
|
|
5964
|
+
}
|
|
5965
|
+
return {
|
|
5966
|
+
source: String(cmcExchangeLiquidity.source ?? ""),
|
|
5967
|
+
available: true,
|
|
5968
|
+
interval: typeof cmcExchangeLiquidity.interval === "string" ? cmcExchangeLiquidity.interval : null,
|
|
5969
|
+
asOfTs: toFiniteNumber(cmcExchangeLiquidity.asOfTs),
|
|
5970
|
+
stale: typeof cmcExchangeLiquidity.stale === "boolean" ? cmcExchangeLiquidity.stale : null,
|
|
5971
|
+
exchangesCount: toFiniteNumber(cmcExchangeLiquidity.exchangesCount),
|
|
5972
|
+
totalVolumeUsd: toFiniteNumber(cmcExchangeLiquidity.totalVolumeUsd),
|
|
5973
|
+
totalVolumeChange24hPct: toFiniteNumber(
|
|
5974
|
+
cmcExchangeLiquidity.totalVolumeChange24hPct
|
|
5975
|
+
),
|
|
5976
|
+
binanceVolumeUsd: toFiniteNumber(cmcExchangeLiquidity.binanceVolumeUsd),
|
|
5977
|
+
binanceVolumeShare: toFiniteNumber(cmcExchangeLiquidity.binanceVolumeShare),
|
|
5978
|
+
topExchangeVolumeShare: toFiniteNumber(
|
|
5979
|
+
cmcExchangeLiquidity.topExchangeVolumeShare
|
|
5980
|
+
),
|
|
5981
|
+
liquidityRegime: typeof cmcExchangeLiquidity.liquidityRegime === "string" ? cmcExchangeLiquidity.liquidityRegime : null
|
|
5982
|
+
};
|
|
5983
|
+
};
|
|
5984
|
+
var buildCmcFearGreedContextFromSignal = (signal) => {
|
|
5985
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
5986
|
+
const relative = toRecord(baseContext?.relative);
|
|
5987
|
+
const cmcFearGreed = toRecord(relative?.cmcFearGreed);
|
|
5988
|
+
if (!cmcFearGreed) {
|
|
5989
|
+
return {
|
|
5990
|
+
source: null,
|
|
5991
|
+
available: false,
|
|
5992
|
+
interval: null,
|
|
5993
|
+
asOfTs: null,
|
|
5994
|
+
stale: null,
|
|
5995
|
+
value: null,
|
|
5996
|
+
valueChange24h: null,
|
|
5997
|
+
valueChange7d: null,
|
|
5998
|
+
classification: null,
|
|
5999
|
+
sentimentRegime: null
|
|
6000
|
+
};
|
|
6001
|
+
}
|
|
5702
6002
|
return {
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
6003
|
+
source: String(cmcFearGreed.source ?? ""),
|
|
6004
|
+
available: true,
|
|
6005
|
+
interval: typeof cmcFearGreed.interval === "string" ? cmcFearGreed.interval : null,
|
|
6006
|
+
asOfTs: toFiniteNumber(cmcFearGreed.asOfTs),
|
|
6007
|
+
stale: typeof cmcFearGreed.stale === "boolean" ? cmcFearGreed.stale : null,
|
|
6008
|
+
value: toFiniteNumber(cmcFearGreed.value),
|
|
6009
|
+
valueChange24h: toFiniteNumber(cmcFearGreed.valueChange24h),
|
|
6010
|
+
valueChange7d: toFiniteNumber(cmcFearGreed.valueChange7d),
|
|
6011
|
+
classification: typeof cmcFearGreed.classification === "string" ? cmcFearGreed.classification : null,
|
|
6012
|
+
sentimentRegime: typeof cmcFearGreed.sentimentRegime === "string" ? cmcFearGreed.sentimentRegime : null
|
|
5706
6013
|
};
|
|
5707
6014
|
};
|
|
6015
|
+
var buildCmcIndexesContextFromSignal = (signal) => {
|
|
6016
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
6017
|
+
const relative = toRecord(baseContext?.relative);
|
|
6018
|
+
const cmcIndexes = toRecord(relative?.cmcIndexes);
|
|
6019
|
+
if (!cmcIndexes) {
|
|
6020
|
+
return {
|
|
6021
|
+
source: null,
|
|
6022
|
+
available: false,
|
|
6023
|
+
interval: null,
|
|
6024
|
+
asOfTs: null,
|
|
6025
|
+
stale: null,
|
|
6026
|
+
cmc100Value: null,
|
|
6027
|
+
cmc100Change24hPct: null,
|
|
6028
|
+
cmc100TopConstituentSymbol: null,
|
|
6029
|
+
cmc100TopConstituentWeightPct: null,
|
|
6030
|
+
cmc20Value: null,
|
|
6031
|
+
cmc20Change24hPct: null,
|
|
6032
|
+
cmc20TopConstituentSymbol: null,
|
|
6033
|
+
cmc20TopConstituentWeightPct: null,
|
|
6034
|
+
cmc20ToCmc100Ratio: null,
|
|
6035
|
+
cmc20ToCmc100RatioChange24hPct: null,
|
|
6036
|
+
indexRegime: null
|
|
6037
|
+
};
|
|
6038
|
+
}
|
|
6039
|
+
return {
|
|
6040
|
+
source: String(cmcIndexes.source ?? ""),
|
|
6041
|
+
available: true,
|
|
6042
|
+
interval: typeof cmcIndexes.interval === "string" ? cmcIndexes.interval : null,
|
|
6043
|
+
asOfTs: toFiniteNumber(cmcIndexes.asOfTs),
|
|
6044
|
+
stale: typeof cmcIndexes.stale === "boolean" ? cmcIndexes.stale : null,
|
|
6045
|
+
cmc100Value: toFiniteNumber(cmcIndexes.cmc100Value),
|
|
6046
|
+
cmc100Change24hPct: toFiniteNumber(cmcIndexes.cmc100Change24hPct),
|
|
6047
|
+
cmc100TopConstituentSymbol: typeof cmcIndexes.cmc100TopConstituentSymbol === "string" ? cmcIndexes.cmc100TopConstituentSymbol : null,
|
|
6048
|
+
cmc100TopConstituentWeightPct: toFiniteNumber(
|
|
6049
|
+
cmcIndexes.cmc100TopConstituentWeightPct
|
|
6050
|
+
),
|
|
6051
|
+
cmc20Value: toFiniteNumber(cmcIndexes.cmc20Value),
|
|
6052
|
+
cmc20Change24hPct: toFiniteNumber(cmcIndexes.cmc20Change24hPct),
|
|
6053
|
+
cmc20TopConstituentSymbol: typeof cmcIndexes.cmc20TopConstituentSymbol === "string" ? cmcIndexes.cmc20TopConstituentSymbol : null,
|
|
6054
|
+
cmc20TopConstituentWeightPct: toFiniteNumber(
|
|
6055
|
+
cmcIndexes.cmc20TopConstituentWeightPct
|
|
6056
|
+
),
|
|
6057
|
+
cmc20ToCmc100Ratio: toFiniteNumber(cmcIndexes.cmc20ToCmc100Ratio),
|
|
6058
|
+
cmc20ToCmc100RatioChange24hPct: toFiniteNumber(
|
|
6059
|
+
cmcIndexes.cmc20ToCmc100RatioChange24hPct
|
|
6060
|
+
),
|
|
6061
|
+
indexRegime: typeof cmcIndexes.indexRegime === "string" ? cmcIndexes.indexRegime : null
|
|
6062
|
+
};
|
|
6063
|
+
};
|
|
6064
|
+
var buildReferenceTradeFlowContextFromSignal = (signal) => {
|
|
6065
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
6066
|
+
const relative = toRecord(baseContext?.relative);
|
|
6067
|
+
const refs = toRecord(relative?.referenceTradeFlow);
|
|
6068
|
+
const primaryReferenceSymbol = typeof refs?.primaryReferenceSymbol === "string" ? refs.primaryReferenceSymbol : null;
|
|
6069
|
+
const tradeFlowBySymbol = toRecord(refs?.tradeFlowBySymbol);
|
|
6070
|
+
const primaryTradeFlow = primaryReferenceSymbol != null ? toRecord(tradeFlowBySymbol?.[primaryReferenceSymbol]) : null;
|
|
6071
|
+
if (!refs) {
|
|
6072
|
+
return {
|
|
6073
|
+
source: null,
|
|
6074
|
+
available: false,
|
|
6075
|
+
primaryReferenceSymbol: null,
|
|
6076
|
+
referenceSymbols: [],
|
|
6077
|
+
primaryTradeFlowBuyPressurePct: null,
|
|
6078
|
+
primaryTradeFlowStale: null
|
|
6079
|
+
};
|
|
6080
|
+
}
|
|
6081
|
+
return {
|
|
6082
|
+
source: String(refs.source ?? ""),
|
|
6083
|
+
available: true,
|
|
6084
|
+
primaryReferenceSymbol,
|
|
6085
|
+
referenceSymbols: Array.isArray(refs.referenceSymbols) ? refs.referenceSymbols.map(String) : [],
|
|
6086
|
+
primaryTradeFlowBuyPressurePct: toFiniteNumber(
|
|
6087
|
+
primaryTradeFlow?.buyPressurePct
|
|
6088
|
+
),
|
|
6089
|
+
primaryTradeFlowStale: typeof primaryTradeFlow?.stale === "boolean" ? primaryTradeFlow.stale : null
|
|
6090
|
+
};
|
|
6091
|
+
};
|
|
6092
|
+
var buildAiMarketContext = (signal) => ({
|
|
6093
|
+
execution: {
|
|
6094
|
+
binanceCoinbaseSpread: buildSpreadContextFromSignal(signal)
|
|
6095
|
+
},
|
|
6096
|
+
participation: {
|
|
6097
|
+
trueDelta: buildTrueDeltaContextFromSignal(signal),
|
|
6098
|
+
tradeFlow: buildTradeFlowContextFromSignal(signal)
|
|
6099
|
+
},
|
|
6100
|
+
relative: {
|
|
6101
|
+
marketBreadth: buildMarketBreadthContextFromSignal(signal),
|
|
6102
|
+
targetVsBtc: buildTargetVsBtcContextFromSignal(signal),
|
|
6103
|
+
btcAltRegime: buildBtcAltRegimeContextFromSignal(signal),
|
|
6104
|
+
cmcGlobal: buildCmcGlobalContextFromSignal(signal),
|
|
6105
|
+
cmcReferenceAssets: buildCmcReferenceAssetsContextFromSignal(signal),
|
|
6106
|
+
cmcExchangeLiquidity: buildCmcExchangeLiquidityContextFromSignal(signal),
|
|
6107
|
+
cmcFearGreed: buildCmcFearGreedContextFromSignal(signal),
|
|
6108
|
+
cmcIndexes: buildCmcIndexesContextFromSignal(signal),
|
|
6109
|
+
referenceTradeFlow: buildReferenceTradeFlowContextFromSignal(signal)
|
|
6110
|
+
}
|
|
6111
|
+
});
|
|
5708
6112
|
|
|
5709
6113
|
// src/strategy/manifests.ts
|
|
5710
6114
|
var import_indicators = require("@tradejs/core/indicators");
|
|
@@ -5713,7 +6117,6 @@ var import_logger2 = require("@tradejs/infra/logger");
|
|
|
5713
6117
|
// src/tradejsConfig.ts
|
|
5714
6118
|
var import_fs = __toESM(require("fs"));
|
|
5715
6119
|
var import_path = __toESM(require("path"));
|
|
5716
|
-
var import_module = require("module");
|
|
5717
6120
|
var import_url = require("url");
|
|
5718
6121
|
var import_config = require("@tradejs/core/config");
|
|
5719
6122
|
var import_logger = require("@tradejs/infra/logger");
|
|
@@ -5729,6 +6132,7 @@ var cachedByCwd = /* @__PURE__ */ new Map();
|
|
|
5729
6132
|
var announcedConfigFile = /* @__PURE__ */ new Set();
|
|
5730
6133
|
var tsNodeRegistered = false;
|
|
5731
6134
|
var tsconfigPathsRegisteredByCwd = /* @__PURE__ */ new Set();
|
|
6135
|
+
var tsconfigPathMatchersByCwd = /* @__PURE__ */ new Map();
|
|
5732
6136
|
var getTradejsProjectCwd = (cwd) => {
|
|
5733
6137
|
const explicit = String(cwd ?? "").trim();
|
|
5734
6138
|
if (explicit) {
|
|
@@ -5758,7 +6162,14 @@ var normalizeConfig = (rawConfig) => {
|
|
|
5758
6162
|
...hooks ? { hooks } : {}
|
|
5759
6163
|
};
|
|
5760
6164
|
};
|
|
5761
|
-
var
|
|
6165
|
+
var getNodeCreateRequire = () => {
|
|
6166
|
+
const builtinModule = process.getBuiltinModule?.("module");
|
|
6167
|
+
if (typeof builtinModule?.createRequire === "function") {
|
|
6168
|
+
return builtinModule.createRequire;
|
|
6169
|
+
}
|
|
6170
|
+
throw new TypeError("module.createRequire is not available");
|
|
6171
|
+
};
|
|
6172
|
+
var getRequireFn = (cwd = getTradejsProjectCwd()) => getNodeCreateRequire()(import_path.default.join(import_path.default.resolve(cwd), "__tradejs_loader__.js"));
|
|
5762
6173
|
var ensureTsNodeRegistered = async () => {
|
|
5763
6174
|
if (tsNodeRegistered) {
|
|
5764
6175
|
return;
|
|
@@ -5768,8 +6179,8 @@ var ensureTsNodeRegistered = async () => {
|
|
|
5768
6179
|
tsNode.register?.({
|
|
5769
6180
|
transpileOnly: true,
|
|
5770
6181
|
compilerOptions: {
|
|
5771
|
-
module: "
|
|
5772
|
-
moduleResolution: "
|
|
6182
|
+
module: "Node16",
|
|
6183
|
+
moduleResolution: "node16"
|
|
5773
6184
|
}
|
|
5774
6185
|
});
|
|
5775
6186
|
tsNodeRegistered = true;
|
|
@@ -5796,6 +6207,42 @@ var ensureTsconfigPathsRegistered = async (cwd = getTradejsProjectCwd()) => {
|
|
|
5796
6207
|
});
|
|
5797
6208
|
tsconfigPathsRegisteredByCwd.add(projectRoot);
|
|
5798
6209
|
};
|
|
6210
|
+
var resolveTsconfigPathModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
|
|
6211
|
+
const projectRoot = getTradejsProjectCwd(cwd);
|
|
6212
|
+
const cachedMatcher = tsconfigPathMatchersByCwd.get(projectRoot);
|
|
6213
|
+
if (cachedMatcher) {
|
|
6214
|
+
const resolved2 = cachedMatcher(moduleName);
|
|
6215
|
+
return resolved2 || null;
|
|
6216
|
+
}
|
|
6217
|
+
const tsconfigPathsModule = await import("tsconfig-paths");
|
|
6218
|
+
const loadConfig = tsconfigPathsModule.loadConfig;
|
|
6219
|
+
const createMatchPath = tsconfigPathsModule.createMatchPath;
|
|
6220
|
+
if (typeof loadConfig !== "function" || typeof createMatchPath !== "function") {
|
|
6221
|
+
return null;
|
|
6222
|
+
}
|
|
6223
|
+
const loadedConfig = loadConfig(projectRoot);
|
|
6224
|
+
if (loadedConfig.resultType !== "success") {
|
|
6225
|
+
return null;
|
|
6226
|
+
}
|
|
6227
|
+
const matchPath = createMatchPath(
|
|
6228
|
+
loadedConfig.absoluteBaseUrl,
|
|
6229
|
+
loadedConfig.paths
|
|
6230
|
+
);
|
|
6231
|
+
const matcher = (requestedModule) => matchPath(requestedModule, void 0, import_fs.default.existsSync, [
|
|
6232
|
+
".ts",
|
|
6233
|
+
".tsx",
|
|
6234
|
+
".mts",
|
|
6235
|
+
".cts",
|
|
6236
|
+
".js",
|
|
6237
|
+
".jsx",
|
|
6238
|
+
".mjs",
|
|
6239
|
+
".cjs",
|
|
6240
|
+
".json"
|
|
6241
|
+
]) || "";
|
|
6242
|
+
tsconfigPathMatchersByCwd.set(projectRoot, matcher);
|
|
6243
|
+
const resolved = matcher(moduleName);
|
|
6244
|
+
return resolved || null;
|
|
6245
|
+
};
|
|
5799
6246
|
var toImportSpecifier = (moduleName) => {
|
|
5800
6247
|
if (moduleName.startsWith("file://")) {
|
|
5801
6248
|
return moduleName;
|
|
@@ -5854,7 +6301,18 @@ var importTradejsModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
|
|
|
5854
6301
|
}
|
|
5855
6302
|
if (isBareModuleSpecifier(normalized)) {
|
|
5856
6303
|
await ensureTsconfigPathsRegistered(cwd);
|
|
5857
|
-
|
|
6304
|
+
try {
|
|
6305
|
+
return requireFn(normalized);
|
|
6306
|
+
} catch (error) {
|
|
6307
|
+
const resolvedByTsconfig = await resolveTsconfigPathModule(
|
|
6308
|
+
normalized,
|
|
6309
|
+
cwd
|
|
6310
|
+
);
|
|
6311
|
+
if (resolvedByTsconfig && resolvedByTsconfig !== normalized) {
|
|
6312
|
+
return requireFn(resolvedByTsconfig);
|
|
6313
|
+
}
|
|
6314
|
+
throw error;
|
|
6315
|
+
}
|
|
5858
6316
|
}
|
|
5859
6317
|
try {
|
|
5860
6318
|
return await import(
|
|
@@ -6124,6 +6582,9 @@ var strategies = new Proxy(
|
|
|
6124
6582
|
}
|
|
6125
6583
|
);
|
|
6126
6584
|
|
|
6585
|
+
// src/strategy/policyProfiles.ts
|
|
6586
|
+
var getStrategyProfileAiAdapter = (manifest, profileId) => manifest?.policyProfiles?.find(({ id }) => id === profileId)?.aiAdapter ?? manifest?.aiAdapter;
|
|
6587
|
+
|
|
6127
6588
|
// src/strategyAdapters/ai.ts
|
|
6128
6589
|
var toRecord2 = (value) => {
|
|
6129
6590
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -6151,13 +6612,13 @@ var buildBaseAiPayload = (signal) => {
|
|
|
6151
6612
|
}
|
|
6152
6613
|
},
|
|
6153
6614
|
figures: trimSeriesDeep(signal.figures ?? {}),
|
|
6154
|
-
indicators:
|
|
6615
|
+
indicators: buildCompactAiIndicatorsSnapshot(signal.indicators),
|
|
6155
6616
|
additionalIndicators: trimSeriesDeep(additionalIndicators)
|
|
6156
6617
|
};
|
|
6157
6618
|
};
|
|
6158
6619
|
var defaultAiAdapter = {};
|
|
6159
|
-
var getStrategyAiAdapter = (strategy) => getStrategyManifest(strategy)
|
|
6160
|
-
var getSignalAiAdapter = (signal) => getStrategyAiAdapter(signal.strategy);
|
|
6620
|
+
var getStrategyAiAdapter = (strategy, profileId) => getStrategyProfileAiAdapter(getStrategyManifest(strategy), profileId) ?? defaultAiAdapter;
|
|
6621
|
+
var getSignalAiAdapter = (signal) => getStrategyAiAdapter(signal.strategy, signal.policyProfileId);
|
|
6161
6622
|
var buildAiPayloadByStrategy = (signal) => {
|
|
6162
6623
|
const basePayload = buildBaseAiPayload(signal);
|
|
6163
6624
|
const adapter = getSignalAiAdapter(signal);
|
|
@@ -6290,24 +6751,44 @@ Input payload structure:
|
|
|
6290
6751
|
- payload.figures:
|
|
6291
6752
|
strategy-specific figures or geometry when available. Fields vary by strategy.
|
|
6292
6753
|
- payload.indicators:
|
|
6293
|
-
indicator dictionaries and series for the coin and BTC; all series are already trimmed to the latest 5 values.
|
|
6754
|
+
historical indicator dictionaries and series for the coin and BTC; all series are already trimmed to the latest 5 values. Treat this block as recent-history transport, not as the primary source of the current shared context.
|
|
6294
6755
|
- payload.additionalIndicators:
|
|
6295
|
-
strategy-specific summary/context fields
|
|
6296
|
-
|
|
6756
|
+
strategy-specific summary/context fields plus the canonical current shared context snapshot.
|
|
6757
|
+
This is not noise; it contains derived fields deliberately passed by the strategy to help the decision.
|
|
6758
|
+
Examples: baseContext, helperFlags, structureContext, volatilitySummary.
|
|
6759
|
+
Always inspect \`payload.additionalIndicators.baseContext\` first for the current shared state:
|
|
6760
|
+
\u2022 \`baseContext.raw\`: current MA, ATR, BB, OBV, price stats, levels, BTC correlation.
|
|
6761
|
+
\u2022 \`baseContext.regime\`: derived trend / volatility / momentum / session regime fields.
|
|
6762
|
+
\u2022 \`baseContext.structure\`: local range position, breakout freshness/quality, level-touch counts, rejection wick context.
|
|
6763
|
+
\u2022 \`baseContext.participation\`: volume/turnover participation, effort-vs-result context, and Binance aggTrades trade-flow when available.
|
|
6764
|
+
\u2022 \`baseContext.relative\`: BTC/ETH relative-strength, benchmark MA bias context, Binance alt-basket breadth, and CoinMarketCap historical global/exchange/index context when available.
|
|
6765
|
+
\u2022 \`baseContext.derivatives\`: Coinalyze-aligned derivatives summary when available.
|
|
6766
|
+
\u2022 \`baseContext.mtf\`: compact multi-timeframe summary plus only the latest few candles for each timeframe.
|
|
6767
|
+
\u2022 \`baseContext.gateFeatures\`: direction-aware, normalized fields derived from baseContext; prefer \`setup\`, \`scores\`, \`confirmations\`, \`conflicts\`, \`risk\`, and \`decisionHints\` for quick gate checks before inspecting raw nested context.
|
|
6297
6768
|
Always inspect \`payload.additionalIndicators.marketContext\` when present:
|
|
6298
|
-
\u2022 \`marketContext.
|
|
6299
|
-
\u2022 \`marketContext.
|
|
6769
|
+
\u2022 \`marketContext.execution.binanceCoinbaseSpread\`: AI-friendly BTC spread view projected from \`payload.additionalIndicators.baseContext.relative.execution.venueSpread\`; \`value=(Coinbase-Binance)/Binance\`, \`bps=value*10000\`.
|
|
6770
|
+
\u2022 \`marketContext.participation.trueDelta\`: Binance taker buy/sell volume delta from kline payload when \`source=kline_taker_volume\`; otherwise absent/unavailable.
|
|
6771
|
+
\u2022 \`marketContext.participation.tradeFlow\`: Binance aggTrades buy/sell pressure buckets when available.
|
|
6772
|
+
\u2022 \`marketContext.relative.marketBreadth\`: equal/volume-weighted alt-basket return, advance/decline ratio, and MA breadth for the configured Binance breadth universe.
|
|
6773
|
+
\u2022 \`marketContext.relative.targetVsBtc\`: target/BTC ratio returns, alpha, beta, and short-window correlation; use it to decide whether the target is leading or lagging BTC in the signal direction.
|
|
6774
|
+
\u2022 \`marketContext.relative.btcAltRegime\`: Binance-derived BTC-vs-alt basket regime, BTC/alt 24h returns, BTC turnover share, and alt dispersion; use it as a broad alt-market risk pocket.
|
|
6775
|
+
\u2022 \`marketContext.relative.cmcGlobal\`: historical CoinMarketCap global market metrics: total/alt market cap, total/alt volume, BTC/ETH dominance and 24h changes, active markets, \`interval\`, and \`altLiquidityRegime\`.
|
|
6776
|
+
\u2022 \`marketContext.relative.cmcReferenceAssets\`: historical CoinMarketCap BTC/ETH market-cap and volume context, ETH/BTC market-cap ratio, ETH-vs-BTC volume ratio, \`interval\`, and \`referenceLiquidityRegime\`.
|
|
6777
|
+
\u2022 \`marketContext.relative.cmcExchangeLiquidity\`: historical CoinMarketCap major-exchange liquidity aggregate: total volume, 24h volume change, Binance share, concentration, and \`liquidityRegime\`.
|
|
6778
|
+
\u2022 \`marketContext.relative.cmcFearGreed\`: historical daily CoinMarketCap Fear & Greed sentiment index: value, classification, 24h/7d value changes, and \`sentimentRegime\`.
|
|
6779
|
+
\u2022 \`marketContext.relative.cmcIndexes\`: historical daily CoinMarketCap CMC100/CMC20 index values, 24h changes, top constituents, CMC20/CMC100 ratio, and \`indexRegime\`.
|
|
6780
|
+
\u2022 \`marketContext.relative.referenceTradeFlow\`: BTC/ETH reference trade-flow summary used for broad market pressure when the target symbol itself is not BTC/ETH.
|
|
6300
6781
|
If those fields exist, use them as a more explicit hint instead of trying to re-derive the same idea from raw lines or points.
|
|
6301
|
-
If \`
|
|
6782
|
+
If \`baseContext.derivatives\` exists, its top-level \`summary\` and \`intervals\` are the primary BTCUSDT Coinalyze benchmark context for the time of the signal. \`secondaryReferenceSymbol\` identifies the ETHUSDT secondary benchmark, and \`referenceContexts\` contains BTCUSDT/ETHUSDT plus configured extra reference symbols such as BNBUSDT/SOLUSDT/TRXUSDT/XRPUSDT. If \`targetContext\` or \`targetDerived\` exists, those fields are the Coinalyze context for the actual target coin; use them as target-specific positioning evidence, but do not infer target-coin derivatives when they are absent.
|
|
6302
6783
|
Key patterns:
|
|
6303
|
-
\u2022
|
|
6304
|
-
\u2022
|
|
6305
|
-
\u2022 strategy service keys are possible as well, for example \`
|
|
6784
|
+
\u2022 current shared state: prefer \`payload.additionalIndicators.baseContext\`
|
|
6785
|
+
\u2022 recent historical series: \`payload.indicators\`
|
|
6786
|
+
\u2022 strategy service keys are possible as well, for example \`touches\`, \`distance\`, timing flags, and other setup-specific summaries
|
|
6306
6787
|
|
|
6307
6788
|
How to analyze, in order:
|
|
6308
6789
|
1. Start with price structure and the setup geometry or context in \`payload.figures\`. This has higher priority than indicators.
|
|
6309
|
-
2. Then use \`payload.additionalIndicators\`
|
|
6310
|
-
3. Then assess confirmation or conflict from the current coin
|
|
6790
|
+
2. Then use \`payload.additionalIndicators.baseContext\` and other explicit strategy-specific context fields.
|
|
6791
|
+
3. Then assess confirmation or conflict from the current shared state and recent coin indicator history.
|
|
6311
6792
|
4. Then evaluate BTC context.
|
|
6312
6793
|
5. Only after that choose \`direction\`, \`quality\`, and whether an extra confirmation level is required.
|
|
6313
6794
|
6. If strong conflicts exist, reduce quality or set direction to \`null\`.
|
|
@@ -6316,13 +6797,24 @@ Explicit conflict rules:
|
|
|
6316
6797
|
- If the figure or price structure is invalid or doubtful, indicators must not rescue the setup.
|
|
6317
6798
|
- If strategy-specific helper fields explicitly say the signal is not confirmed yet, lacks margin, or requires waiting, do not overstate quality.
|
|
6318
6799
|
- If the structure is acceptable but BTC or key indicators noticeably conflict, quality is usually \`<= 3\`.
|
|
6319
|
-
- If \`
|
|
6320
|
-
- If \`
|
|
6321
|
-
- If \`
|
|
6322
|
-
- If \`
|
|
6323
|
-
-
|
|
6324
|
-
- If \`marketContext.binanceCoinbaseSpread.available=true\` and \`severity=elevated/wide\`, treat it as cross-exchange divergence or BTC liquidity risk. Do not use the spread as a standalone long/short signal, but reduce confidence or require more confirmation when the rest of the structure is weak or BTC context conflicts.
|
|
6325
|
-
- If \`marketContext.binanceCoinbaseSpread\` is missing or \`available=false\`, do not infer anything from Binance/Coinbase spread and do not penalize the signal just because it is absent.
|
|
6800
|
+
- If \`baseContext.derivatives.referenceContexts\` exists, check \`primaryReferenceSymbol\` first as the BTC benchmark, then compare \`secondaryReferenceSymbol\`/ETHUSDT and any target-specific \`targetDerived\`. If \`targetDerived\` exists, compare it to the primary reference instead of treating reference pressure as the target coin's own pressure.
|
|
6801
|
+
- If top-level \`baseContext.derivatives.summary.riskFlags\` contains \`crowded_long\` for a LONG or \`crowded_short\` for a SHORT, treat that as broad-market crowded positioning. If \`targetDerived.riskFlags\` contains the same directional crowding, treat that as target-specific crowded positioning.
|
|
6802
|
+
- If top-level \`baseContext.derivatives.summary.directionAligned=false\`, explicitly mention the broad-market derivatives conflict in \`confirmations\` or \`qualityReason\`. If \`targetDerived.directionAligned=false\`, explicitly mention the target-specific derivatives conflict.
|
|
6803
|
+
- If \`baseContext.derivatives\` is absent, stale, or \`missing_derivatives\`, do not infer Coinalyze conclusions and do not penalize the signal just because that data is missing.
|
|
6804
|
+
- Use \`baseContext.regime.session\` directly as the canonical session/liquidity regime: asia is often thinner, europe/us are more active, and overlaps can amplify both momentum and noise. Do not reject a signal solely because of session, but mention clear session support or conflict in \`confirmations\` or \`qualityReason\`.
|
|
6805
|
+
- If \`marketContext.execution.binanceCoinbaseSpread.available=true\` and \`severity=elevated/wide\`, treat it as cross-exchange divergence or BTC liquidity risk. Do not use the spread as a standalone long/short signal, but reduce confidence or require more confirmation when the rest of the structure is weak or BTC context conflicts.
|
|
6806
|
+
- If \`marketContext.execution.binanceCoinbaseSpread\` is missing or \`available=false\`, do not infer anything from Binance/Coinbase spread and do not penalize the signal just because it is absent.
|
|
6807
|
+
- If \`marketContext.participation.trueDelta.available=true\`, use it as better participation evidence than OHLCV-derived proxy delta; still do not let delta override invalid price structure.
|
|
6808
|
+
- If \`marketContext.participation.tradeFlow.available=true\` and \`stale=false\`, use it as direct lower-timeframe participation evidence. Treat stale or missing tradeFlow as absent, not as negative evidence.
|
|
6809
|
+
- If \`marketContext.relative.marketBreadth.available=true\` and \`stale=false\`, use it as broad alt-market support/conflict. Breadth is contextual; do not let it override the target symbol structure.
|
|
6810
|
+
- If \`marketContext.relative.targetVsBtc.available=true\`, treat positive target/BTC ratio trend as support for alt LONGs and negative ratio trend as support for alt SHORTs; ignore it when the target structure is stronger and clearly explains the setup.
|
|
6811
|
+
- If \`marketContext.relative.btcAltRegime.available=true\` and \`stale=false\`, treat \`alt_lead\`/\`risk_on\` as broad support for alt LONGs and \`btc_lead\`/\`risk_off\` as pressure against alt LONGs or support for cautious alt SHORTs. Do not use it as a standalone entry reason.
|
|
6812
|
+
- If \`marketContext.relative.cmcGlobal.available=true\` and \`stale=false\`, use falling alt market cap/volume or rising BTC dominance as broad risk pressure for alt LONGs. Treat missing CMC history as absent context, not a bearish signal.
|
|
6813
|
+
- If \`marketContext.relative.cmcReferenceAssets.available=true\` and \`stale=false\`, use \`eth_led\` as broad support for ETH/high-beta alt strength and \`btc_led\`/\`thin\` as broad caution. Do not describe BTC/ETH reference history as target-symbol flow.
|
|
6814
|
+
- If \`marketContext.relative.cmcExchangeLiquidity.available=true\` and \`stale=false\`, treat \`contracting\`, \`thin\`, or \`concentrated\` as broad liquidity risk; \`expanding\` or \`balanced\` supports cleaner execution context but is not a standalone entry reason.
|
|
6815
|
+
- If \`marketContext.relative.cmcFearGreed.available=true\` and \`stale=false\`, use \`risk_on\` as broad support for LONGs and \`risk_off\`/\`capitulation\` as broad pressure. Treat \`euphoric\` as overheating/chase caution, not as standalone SHORT proof.
|
|
6816
|
+
- If \`marketContext.relative.cmcIndexes.available=true\` and \`stale=false\`, use \`top20_led\` as broad support for mega-cap leadership, \`large_cap_led\` as broader CMC100 participation, and \`risk_off\` as broad pressure. Do not use CMC index history as a standalone entry reason.
|
|
6817
|
+
- If \`marketContext.relative.referenceTradeFlow.available=true\`, treat BTC/ETH trade-flow as broad market context only. For alt symbols, do not describe it as the target coin's own flow.
|
|
6326
6818
|
- If the current signal is not confirmed (\`direction=null\`), name the main reason briefly in \`comment\`.
|
|
6327
6819
|
If you use the structured fields, include the main reason in \`qualityReason\` or \`triggerInvalidation\`.
|
|
6328
6820
|
|
|
@@ -6373,6 +6865,18 @@ Trade payload:
|
|
|
6373
6865
|
${JSON.stringify(payload)}
|
|
6374
6866
|
${buildAiHumanPromptAddonByStrategy(signal, payload)}
|
|
6375
6867
|
`;
|
|
6868
|
+
var getAiInvocationError = (error) => {
|
|
6869
|
+
const details = error instanceof Error && error.message.trim() ? error.message.trim() : String(error);
|
|
6870
|
+
const isEmptyCompletion = error instanceof TypeError && /Cannot read properties of undefined \(reading ['"]message['"]\)/.test(
|
|
6871
|
+
details
|
|
6872
|
+
);
|
|
6873
|
+
const wrapped = new Error(
|
|
6874
|
+
isEmptyCompletion ? "AI provider returned an empty chat completion" : `AI model invocation failed: ${details}`
|
|
6875
|
+
);
|
|
6876
|
+
wrapped.cause = error;
|
|
6877
|
+
return wrapped;
|
|
6878
|
+
};
|
|
6879
|
+
var isEmptyResponseContent = (content) => typeof content === "string" ? content.trim().length === 0 : Object.keys(content).length === 0;
|
|
6376
6880
|
var DEFAULT_AI_MODEL = "openai/gpt-5-mini";
|
|
6377
6881
|
var userSettingsCache = /* @__PURE__ */ new Map();
|
|
6378
6882
|
var aiModelCache = /* @__PURE__ */ new Map();
|
|
@@ -6493,10 +6997,17 @@ var runAiPrompt = async ({ systemPrompt, humanPrompt }, options = {}) => {
|
|
|
6493
6997
|
]
|
|
6494
6998
|
})
|
|
6495
6999
|
);
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
)
|
|
7000
|
+
let response;
|
|
7001
|
+
try {
|
|
7002
|
+
response = await model.invoke(messages);
|
|
7003
|
+
} catch (error) {
|
|
7004
|
+
throw getAiInvocationError(error);
|
|
7005
|
+
}
|
|
7006
|
+
const responseContent = normalizeResponseContent(response?.content);
|
|
7007
|
+
if (isEmptyResponseContent(responseContent)) {
|
|
7008
|
+
throw new Error("AI provider returned an empty chat completion");
|
|
7009
|
+
}
|
|
7010
|
+
const parsed = parseAIResponse(responseContent);
|
|
6500
7011
|
const normalized = normalizeAnalysis(parsed);
|
|
6501
7012
|
if (!options.signal) {
|
|
6502
7013
|
return normalized;
|
|
@@ -6533,6 +7044,27 @@ var import_puppeteer = __toESM(require("puppeteer"));
|
|
|
6533
7044
|
var import_async = require("@tradejs/core/async");
|
|
6534
7045
|
var import_logger3 = require("@tradejs/infra/logger");
|
|
6535
7046
|
var import_redis2 = require("@tradejs/infra/redis");
|
|
7047
|
+
|
|
7048
|
+
// src/dashboardUrl.ts
|
|
7049
|
+
var buildDashboardUrl = ({
|
|
7050
|
+
baseUrl,
|
|
7051
|
+
provider = "bybit",
|
|
7052
|
+
universe = "crypto",
|
|
7053
|
+
symbol,
|
|
7054
|
+
interval,
|
|
7055
|
+
searchParams = {}
|
|
7056
|
+
}) => {
|
|
7057
|
+
const url = new URL(
|
|
7058
|
+
`/routes/dashboard/${provider}/${universe}/${symbol}/${interval}`,
|
|
7059
|
+
baseUrl
|
|
7060
|
+
);
|
|
7061
|
+
for (const [key, value] of Object.entries(searchParams)) {
|
|
7062
|
+
url.searchParams.set(key, value);
|
|
7063
|
+
}
|
|
7064
|
+
return url.toString();
|
|
7065
|
+
};
|
|
7066
|
+
|
|
7067
|
+
// src/screenshot.ts
|
|
6536
7068
|
var { APP_URL } = process.env;
|
|
6537
7069
|
var SCREENSHOT_NAVIGATION_ATTEMPTS = 3;
|
|
6538
7070
|
var SCREENSHOT_NAVIGATION_RETRY_DELAY_MS = 2e3;
|
|
@@ -6641,7 +7173,18 @@ var screenDashboard = async (signal, projectRoot, userName = "root") => {
|
|
|
6641
7173
|
`Failed to create screenshot session token for ${userName}`
|
|
6642
7174
|
);
|
|
6643
7175
|
}
|
|
6644
|
-
const dashboardUrl =
|
|
7176
|
+
const dashboardUrl = buildDashboardUrl({
|
|
7177
|
+
baseUrl: screenshotBaseUrl,
|
|
7178
|
+
universe: signal.universe ?? "crypto",
|
|
7179
|
+
symbol,
|
|
7180
|
+
interval,
|
|
7181
|
+
searchParams: {
|
|
7182
|
+
signalId,
|
|
7183
|
+
autoZoom: "true",
|
|
7184
|
+
screenshot: "1",
|
|
7185
|
+
screenshotToken
|
|
7186
|
+
}
|
|
7187
|
+
});
|
|
6645
7188
|
const maskedDashboardUrl = maskTokenInUrl(dashboardUrl);
|
|
6646
7189
|
import_logger3.logger.info(
|
|
6647
7190
|
"screenshot start: %s %sm url=%s path=%s",
|
|
@@ -6949,6 +7492,25 @@ var describeErrorValue2 = (value) => {
|
|
|
6949
7492
|
return String(value);
|
|
6950
7493
|
};
|
|
6951
7494
|
var normalizeQuality = (value) => typeof value === "number" ? Math.max(1, Math.min(5, Math.round(value))) : null;
|
|
7495
|
+
var formatOrderValue = (value) => {
|
|
7496
|
+
const rounded = Number(value.toFixed(2));
|
|
7497
|
+
return Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(2);
|
|
7498
|
+
};
|
|
7499
|
+
var getDisplayDecision = (signalDirection, analysis, fallback) => {
|
|
7500
|
+
if (!analysis && !fallback) {
|
|
7501
|
+
return void 0;
|
|
7502
|
+
}
|
|
7503
|
+
if (analysis?.direction != null && analysis.direction !== signalDirection) {
|
|
7504
|
+
return "rejected";
|
|
7505
|
+
}
|
|
7506
|
+
if (analysis?.needRetest === true) {
|
|
7507
|
+
return "pending";
|
|
7508
|
+
}
|
|
7509
|
+
if (analysis?.direction != null && analysis.direction === signalDirection) {
|
|
7510
|
+
return "approved";
|
|
7511
|
+
}
|
|
7512
|
+
return fallback;
|
|
7513
|
+
};
|
|
6952
7514
|
var formatAnalysisLevel = (value) => {
|
|
6953
7515
|
if (value == null || !Number.isFinite(value)) return null;
|
|
6954
7516
|
if (Math.abs(value) >= 1e3) {
|
|
@@ -6996,24 +7558,17 @@ var formatOrderSkipReason = (reason) => {
|
|
|
6996
7558
|
}
|
|
6997
7559
|
return reason;
|
|
6998
7560
|
};
|
|
6999
|
-
var
|
|
7000
|
-
if (Array.isArray(value)) {
|
|
7001
|
-
const last = value[value.length - 1];
|
|
7002
|
-
return typeof last === "number" ? last : void 0;
|
|
7003
|
-
}
|
|
7004
|
-
return typeof value === "number" ? value : void 0;
|
|
7005
|
-
};
|
|
7006
|
-
var getAiQualityLine = (analysis) => {
|
|
7561
|
+
var getAiQualityLine = (analysis, label = "AI Quality") => {
|
|
7007
7562
|
const quality = normalizeQuality(analysis?.quality);
|
|
7008
7563
|
if (!quality) return null;
|
|
7009
7564
|
const approvedCurrentDirection = analysis?.direction != null && analysis.direction !== null;
|
|
7010
7565
|
if (quality >= 4 && approvedCurrentDirection) {
|
|
7011
|
-
return `\u{1F7E2}
|
|
7566
|
+
return `\u{1F7E2} ${label}: ${quality}/5`;
|
|
7012
7567
|
}
|
|
7013
7568
|
if (quality === 3 && approvedCurrentDirection) {
|
|
7014
|
-
return `\u{1F7E1}
|
|
7569
|
+
return `\u{1F7E1} ${label}: ${quality}/5`;
|
|
7015
7570
|
}
|
|
7016
|
-
return `\u{1F534}
|
|
7571
|
+
return `\u{1F534} ${label}: ${quality}/5`;
|
|
7017
7572
|
};
|
|
7018
7573
|
var getTelegramErrorReason = (data) => {
|
|
7019
7574
|
if (data && typeof data === "object") {
|
|
@@ -7139,26 +7694,59 @@ var sendTextToTG = async (message, options = {}) => {
|
|
|
7139
7694
|
chatId
|
|
7140
7695
|
});
|
|
7141
7696
|
};
|
|
7142
|
-
var
|
|
7697
|
+
var sendDocumentToTG = async (document, options = {}) => {
|
|
7698
|
+
const { token, chatId } = await getTelegramSettings(options.userName);
|
|
7699
|
+
const body = new FormData();
|
|
7700
|
+
const fileContent = typeof document.content === "string" ? document.content : (() => {
|
|
7701
|
+
const bytes = new Uint8Array(document.content.byteLength);
|
|
7702
|
+
bytes.set(document.content);
|
|
7703
|
+
return bytes;
|
|
7704
|
+
})();
|
|
7705
|
+
body.set("chat_id", String(chatId || ""));
|
|
7706
|
+
body.set(
|
|
7707
|
+
"document",
|
|
7708
|
+
new File([fileContent], document.filename, {
|
|
7709
|
+
type: "application/json"
|
|
7710
|
+
})
|
|
7711
|
+
);
|
|
7712
|
+
if (document.caption?.trim()) {
|
|
7713
|
+
body.set("caption", document.caption.trim());
|
|
7714
|
+
body.set("parse_mode", "HTML");
|
|
7715
|
+
}
|
|
7716
|
+
const data = await requestTelegram({
|
|
7717
|
+
method: "sendDocument",
|
|
7718
|
+
token,
|
|
7719
|
+
init: {
|
|
7720
|
+
method: "POST",
|
|
7721
|
+
body
|
|
7722
|
+
}
|
|
7723
|
+
});
|
|
7724
|
+
import_logger4.logger.info(
|
|
7725
|
+
"tg sendDocument: %s",
|
|
7726
|
+
data?.ok ? "sent" : getTelegramErrorReason(data)
|
|
7727
|
+
);
|
|
7728
|
+
return data;
|
|
7729
|
+
};
|
|
7730
|
+
var formatMessage = (signal, analysis, options = {}) => {
|
|
7143
7731
|
const {
|
|
7144
7732
|
symbol,
|
|
7145
7733
|
direction,
|
|
7146
7734
|
strategy,
|
|
7147
7735
|
orderStatus,
|
|
7148
7736
|
orderSkipReason,
|
|
7737
|
+
orderFailureReason,
|
|
7738
|
+
orderQty,
|
|
7739
|
+
orderValue,
|
|
7149
7740
|
isConfigFromBacktest,
|
|
7150
7741
|
ml,
|
|
7151
7742
|
prices: { currentPrice, takeProfitPrice, stopLossPrice, riskRatio },
|
|
7152
|
-
indicators,
|
|
7153
7743
|
additionalIndicators
|
|
7154
7744
|
} = signal;
|
|
7745
|
+
const userName = options.userName || "root";
|
|
7155
7746
|
try {
|
|
7156
7747
|
const lines = [];
|
|
7157
7748
|
const distance = additionalIndicators?.distance;
|
|
7158
7749
|
const touches = additionalIndicators?.touches;
|
|
7159
|
-
const correlation = getLastNumber(indicators.correlation);
|
|
7160
|
-
const atrPct = getLastNumber(indicators.atrPct);
|
|
7161
|
-
const spread = getLastNumber(indicators.spread);
|
|
7162
7750
|
const formatPrices = () => {
|
|
7163
7751
|
const tpPercent = Math.abs(
|
|
7164
7752
|
(takeProfitPrice - currentPrice) / currentPrice * 100
|
|
@@ -7166,8 +7754,10 @@ var formatMessage = (signal, analysis) => {
|
|
|
7166
7754
|
const slPercent = Math.abs((stopLossPrice - currentPrice) / currentPrice * 100).toFixed(
|
|
7167
7755
|
2
|
|
7168
7756
|
) + "%";
|
|
7757
|
+
const resolvedOrderValue = typeof orderValue === "number" && Number.isFinite(orderValue) ? orderValue : typeof orderQty === "number" && Number.isFinite(orderQty) ? orderQty * currentPrice : null;
|
|
7169
7758
|
const prices = [
|
|
7170
7759
|
`Price: <b>${(0, import_math.formatNumber)(currentPrice)}</b>`,
|
|
7760
|
+
resolvedOrderValue != null ? `Value: <b>${formatOrderValue(resolvedOrderValue)}$</b>` : null,
|
|
7171
7761
|
`TP: <b>${(0, import_math.formatNumber)(takeProfitPrice)}</b> (${tpPercent})`,
|
|
7172
7762
|
`SL: <b>${(0, import_math.formatNumber)(stopLossPrice)}</b> (${slPercent})`,
|
|
7173
7763
|
`R:R = <b>${riskRatio.toFixed(2)}</b>`
|
|
@@ -7193,6 +7783,8 @@ var formatMessage = (signal, analysis) => {
|
|
|
7193
7783
|
lines.push(
|
|
7194
7784
|
`Skip reason: <b>${escapeHtml(formatOrderSkipReason(orderSkipReason))}</b>`
|
|
7195
7785
|
);
|
|
7786
|
+
} else if (orderStatus === "failed" && orderFailureReason) {
|
|
7787
|
+
lines.push(`Reason: <code>${escapeHtml(orderFailureReason)}</code>`);
|
|
7196
7788
|
}
|
|
7197
7789
|
}
|
|
7198
7790
|
if (isConfigFromBacktest) {
|
|
@@ -7205,17 +7797,25 @@ var formatMessage = (signal, analysis) => {
|
|
|
7205
7797
|
`${ml.passed ? "\u{1F7E2} ML: PASS" : "\u{1F534} ML: FAIL"} (${ml.probability.toFixed(3)} / ${ml.threshold.toFixed(2)})`
|
|
7206
7798
|
);
|
|
7207
7799
|
}
|
|
7208
|
-
const
|
|
7209
|
-
if (
|
|
7210
|
-
|
|
7800
|
+
const gateAnalysis = analysis?.gateAnalysis && typeof analysis.gateAnalysis === "object" && !Array.isArray(analysis.gateAnalysis) ? analysis.gateAnalysis : null;
|
|
7801
|
+
if (gateAnalysis) {
|
|
7802
|
+
const gateQualityLine = getAiQualityLine(gateAnalysis, "Gate Quality");
|
|
7803
|
+
if (gateQualityLine) {
|
|
7804
|
+
lines.push(gateQualityLine);
|
|
7805
|
+
}
|
|
7806
|
+
const llmQualityLine = getAiQualityLine(analysis, "LLM Quality");
|
|
7807
|
+
if (llmQualityLine) {
|
|
7808
|
+
lines.push(llmQualityLine);
|
|
7809
|
+
}
|
|
7810
|
+
} else {
|
|
7811
|
+
const aiQualityLine = getAiQualityLine(analysis);
|
|
7812
|
+
if (aiQualityLine) {
|
|
7813
|
+
lines.push(aiQualityLine);
|
|
7814
|
+
}
|
|
7211
7815
|
}
|
|
7212
7816
|
lines.push("");
|
|
7213
|
-
if (correlation) {
|
|
7214
|
-
lines.push(`BTC correlation: ${correlation}`);
|
|
7215
|
-
}
|
|
7216
7817
|
const prices = formatPrices();
|
|
7217
7818
|
if (prices) {
|
|
7218
|
-
lines.push("");
|
|
7219
7819
|
lines.push(prices);
|
|
7220
7820
|
}
|
|
7221
7821
|
};
|
|
@@ -7229,9 +7829,17 @@ var formatMessage = (signal, analysis) => {
|
|
|
7229
7829
|
var sendSignal = async (signal, imgInterval, analysis, options = {}) => {
|
|
7230
7830
|
const { symbol, signalId, interval } = signal;
|
|
7231
7831
|
const { token, chatId } = await getTelegramSettings(options.userName);
|
|
7232
|
-
const message = formatMessage(signal, analysis
|
|
7832
|
+
const message = formatMessage(signal, analysis, {
|
|
7833
|
+
userName: options.userName
|
|
7834
|
+
});
|
|
7233
7835
|
const publicAppUrl = APP_URL2?.startsWith("https") ? APP_URL2 : null;
|
|
7234
|
-
const dashboardUrl = publicAppUrl ?
|
|
7836
|
+
const dashboardUrl = publicAppUrl ? buildDashboardUrl({
|
|
7837
|
+
baseUrl: publicAppUrl,
|
|
7838
|
+
universe: signal.universe ?? "crypto",
|
|
7839
|
+
symbol,
|
|
7840
|
+
interval,
|
|
7841
|
+
searchParams: { signalId }
|
|
7842
|
+
}) : null;
|
|
7235
7843
|
const actionButtons = [
|
|
7236
7844
|
dashboardUrl ? { text: "Dashboard", url: dashboardUrl } : null
|
|
7237
7845
|
].filter(Boolean);
|
|
@@ -7324,9 +7932,19 @@ var formatAnalysisMessage = (signal, analysis) => {
|
|
|
7324
7932
|
if (quality) {
|
|
7325
7933
|
lines.push(`Quality: <b>${quality}/5</b>`);
|
|
7326
7934
|
}
|
|
7327
|
-
|
|
7935
|
+
const gateDecision = getDisplayDecision(
|
|
7936
|
+
signal.direction,
|
|
7937
|
+
analysis.gateAnalysis,
|
|
7938
|
+
analysis.gateDecision
|
|
7939
|
+
);
|
|
7940
|
+
const llmDecision = getDisplayDecision(
|
|
7941
|
+
signal.direction,
|
|
7942
|
+
analysis,
|
|
7943
|
+
analysis.llmDecision
|
|
7944
|
+
);
|
|
7945
|
+
if (gateDecision && llmDecision) {
|
|
7328
7946
|
lines.push(
|
|
7329
|
-
`Gate vs LLM: <b>${
|
|
7947
|
+
`Gate vs LLM: <b>${gateDecision === llmDecision ? "aligned" : "conflict"}</b> (gate ${gateDecision}, LLM ${llmDecision})`
|
|
7330
7948
|
);
|
|
7331
7949
|
}
|
|
7332
7950
|
const happeningText = takeUniqueAnalysisText(
|
|
@@ -7371,19 +7989,6 @@ var formatAnalysisMessage = (signal, analysis) => {
|
|
|
7371
7989
|
if (btcText) {
|
|
7372
7990
|
lines.push(`BTC context: ${escapeHtml(btcText)}`);
|
|
7373
7991
|
}
|
|
7374
|
-
const levels = [];
|
|
7375
|
-
if (typeof analysis.takeProfitPrice === "number") {
|
|
7376
|
-
levels.push(`TP <b>${formatAnalysisLevel(analysis.takeProfitPrice)}</b>`);
|
|
7377
|
-
}
|
|
7378
|
-
if (typeof analysis.stopLossPrice === "number") {
|
|
7379
|
-
levels.push(`SL <b>${formatAnalysisLevel(analysis.stopLossPrice)}</b>`);
|
|
7380
|
-
}
|
|
7381
|
-
if (typeof analysis.retestPrice === "number") {
|
|
7382
|
-
levels.push(`Retest <b>${formatAnalysisLevel(analysis.retestPrice)}</b>`);
|
|
7383
|
-
}
|
|
7384
|
-
if (levels.length > 0) {
|
|
7385
|
-
lines.push(`Levels: ${levels.join(" | ")}`);
|
|
7386
|
-
}
|
|
7387
7992
|
return lines.join("\n");
|
|
7388
7993
|
};
|
|
7389
7994
|
var sendSignalAnalysis = async (signal, analysis, options = {}) => {
|
|
@@ -7410,10 +8015,35 @@ var sendSignalAnalysis = async (signal, analysis, options = {}) => {
|
|
|
7410
8015
|
|
|
7411
8016
|
// src/cli.ts
|
|
7412
8017
|
var getProjectRoot2 = () => getTradejsProjectCwd();
|
|
8018
|
+
var escapeHtml2 = (value) => value == null ? "" : String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
8019
|
+
var formatOptionalNumber = (value) => {
|
|
8020
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
8021
|
+
return "n/a";
|
|
8022
|
+
}
|
|
8023
|
+
const normalized = Number(value.toFixed(8));
|
|
8024
|
+
return Number.isInteger(normalized) ? String(normalized) : String(normalized);
|
|
8025
|
+
};
|
|
8026
|
+
var formatOptionalTimestamp = (value) => {
|
|
8027
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
8028
|
+
return "n/a";
|
|
8029
|
+
}
|
|
8030
|
+
return new Date(value).toISOString();
|
|
8031
|
+
};
|
|
8032
|
+
var isEnoentError = (error) => error?.code === "ENOENT";
|
|
7413
8033
|
var cleanFiles = async (dir) => {
|
|
7414
8034
|
let completed = 0;
|
|
7415
8035
|
const projectRoot = getProjectRoot2();
|
|
7416
|
-
|
|
8036
|
+
let files = [];
|
|
8037
|
+
try {
|
|
8038
|
+
files = await (0, import_files.getFiles)(dir, projectRoot);
|
|
8039
|
+
} catch (error) {
|
|
8040
|
+
if (isEnoentError(error)) {
|
|
8041
|
+
import_logger5.logger.info(import_chalk.default.yellow("clean:", dir));
|
|
8042
|
+
import_logger5.logger.info("");
|
|
8043
|
+
return;
|
|
8044
|
+
}
|
|
8045
|
+
throw error;
|
|
8046
|
+
}
|
|
7417
8047
|
const bar = new import_progress.default(":current/:total [:bar][:percent] :eta(s)", {
|
|
7418
8048
|
total: files.length,
|
|
7419
8049
|
width: 30
|
|
@@ -7421,12 +8051,18 @@ var cleanFiles = async (dir) => {
|
|
|
7421
8051
|
import_logger5.logger.info(import_chalk.default.yellow("clean:", dir));
|
|
7422
8052
|
for await (const file of files) {
|
|
7423
8053
|
completed++;
|
|
7424
|
-
const fullPath =
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
8054
|
+
const fullPath = import_path3.default.join(projectRoot, dir, file);
|
|
8055
|
+
try {
|
|
8056
|
+
const stat = await import_promises2.default.lstat(fullPath);
|
|
8057
|
+
if (stat.isDirectory()) {
|
|
8058
|
+
await import_promises2.default.rm(fullPath, { recursive: true, force: true });
|
|
8059
|
+
} else {
|
|
8060
|
+
await import_promises2.default.unlink(fullPath);
|
|
8061
|
+
}
|
|
8062
|
+
} catch (error) {
|
|
8063
|
+
if (!isEnoentError(error)) {
|
|
8064
|
+
throw error;
|
|
8065
|
+
}
|
|
7430
8066
|
}
|
|
7431
8067
|
if (completed % 100 === 0 || completed === files.length) {
|
|
7432
8068
|
bar.tick(completed === files.length ? completed % 100 : 100);
|
|
@@ -7471,28 +8107,58 @@ var update = async (connector, interval, tickers, preloadDays = import_constants
|
|
|
7471
8107
|
);
|
|
7472
8108
|
const preloadEnd = Math.trunc(options.preloadEnd ?? (0, import_time.getTimestamp)());
|
|
7473
8109
|
const connectorLabel = String(options.connectorLabel || "").trim();
|
|
7474
|
-
const preloadLabel = options.preloadStart != null || options.preloadEnd != null ? `preloadStart=${preloadStart}, preloadEnd=${preloadEnd}` : `preloadDays=${preloadDays}`;
|
|
8110
|
+
const preloadLabel = options.preloadStart != null || options.preloadEnd != null ? `preloadStart=${formatOptionalTimestamp(preloadStart)}, preloadEnd=${formatOptionalTimestamp(preloadEnd)}` : `preloadDays=${preloadDays}`;
|
|
7475
8111
|
if (preloadStart >= preloadEnd) {
|
|
7476
8112
|
throw new Error(
|
|
7477
8113
|
`Invalid update preload window: start (${preloadStart}) must be less than end (${preloadEnd})`
|
|
7478
8114
|
);
|
|
7479
8115
|
}
|
|
8116
|
+
let queue = [...new Set(tickers.slice())];
|
|
8117
|
+
if (!queue.includes("BTCUSDT")) {
|
|
8118
|
+
queue.unshift("BTCUSDT");
|
|
8119
|
+
}
|
|
8120
|
+
const intervalMinutes = Number(interval);
|
|
8121
|
+
if (options.skipCovered && connectorLabel && Number.isFinite(intervalMinutes) && intervalMinutes > 0) {
|
|
8122
|
+
const edges = await (0, import_timescale.getDataEdgesForSymbols)(
|
|
8123
|
+
connectorLabel,
|
|
8124
|
+
queue,
|
|
8125
|
+
Math.floor(intervalMinutes)
|
|
8126
|
+
);
|
|
8127
|
+
const initialCount = queue.length;
|
|
8128
|
+
queue = queue.filter((symbol) => {
|
|
8129
|
+
const edge = edges.get(String(symbol).toUpperCase());
|
|
8130
|
+
return !edge || edge.min === void 0 || edge.max === void 0 || edge.min > preloadStart || edge.max < preloadEnd;
|
|
8131
|
+
});
|
|
8132
|
+
const skippedCount = initialCount - queue.length;
|
|
8133
|
+
if (skippedCount > 0) {
|
|
8134
|
+
import_logger5.logger.info(
|
|
8135
|
+
import_chalk.default.gray(
|
|
8136
|
+
`update ${connectorLabel}: skip ${skippedCount}/${initialCount} cached symbols for interval=${interval}`
|
|
8137
|
+
)
|
|
8138
|
+
);
|
|
8139
|
+
}
|
|
8140
|
+
}
|
|
8141
|
+
if (queue.length === 0) {
|
|
8142
|
+
import_logger5.logger.info(
|
|
8143
|
+
import_chalk.default.yellow(
|
|
8144
|
+
`update: 0 (connector=${connectorLabel || "unknown"}, interval=${interval}, klineConcurrency=${KLINE_CONCURRENCY_LIMIT}, ${preloadLabel})`
|
|
8145
|
+
)
|
|
8146
|
+
);
|
|
8147
|
+
import_logger5.logger.info("");
|
|
8148
|
+
return;
|
|
8149
|
+
}
|
|
7480
8150
|
const bar = new import_progress.default(
|
|
7481
8151
|
":current/:total [:bar][:percent] :eta(s) :symbol",
|
|
7482
8152
|
{
|
|
7483
|
-
total:
|
|
8153
|
+
total: queue.length,
|
|
7484
8154
|
width: 30
|
|
7485
8155
|
}
|
|
7486
8156
|
);
|
|
7487
8157
|
import_logger5.logger.info(
|
|
7488
8158
|
import_chalk.default.yellow(
|
|
7489
|
-
`update: ${
|
|
8159
|
+
`update: ${queue.length} (connector=${connectorLabel || "unknown"}, interval=${interval}, klineConcurrency=${KLINE_CONCURRENCY_LIMIT}, ${preloadLabel})`
|
|
7490
8160
|
)
|
|
7491
8161
|
);
|
|
7492
|
-
const queue = tickers.slice();
|
|
7493
|
-
if (!queue.includes("BTCUSDT")) {
|
|
7494
|
-
queue.unshift("BTCUSDT");
|
|
7495
|
-
}
|
|
7496
8162
|
await (0, import_async3.runWithConcurrency)(queue, KLINE_CONCURRENCY_LIMIT, async (symbol) => {
|
|
7497
8163
|
try {
|
|
7498
8164
|
await connector.kline({
|
|
@@ -7521,6 +8187,8 @@ var getCLILevelColor = (level) => {
|
|
|
7521
8187
|
return import_chalk.default.green;
|
|
7522
8188
|
case "warning":
|
|
7523
8189
|
return import_chalk.default.yellow;
|
|
8190
|
+
case "neutral":
|
|
8191
|
+
return import_chalk.default.gray;
|
|
7524
8192
|
case "error":
|
|
7525
8193
|
return import_chalk.default.red;
|
|
7526
8194
|
}
|
|
@@ -7532,18 +8200,18 @@ var drawStatInCLI = (stat, keys) => {
|
|
|
7532
8200
|
return color(formatted);
|
|
7533
8201
|
});
|
|
7534
8202
|
};
|
|
7535
|
-
var scanner = async (connector, limit) => {
|
|
7536
|
-
const data = await connector.getTickers();
|
|
8203
|
+
var scanner = async (connector, limit, query) => {
|
|
8204
|
+
const data = await connector.getTickers(query);
|
|
7537
8205
|
const tickers = (0, import_tickers.getTopTickers)(data, limit);
|
|
7538
8206
|
return tickers.map(({ value }) => value);
|
|
7539
8207
|
};
|
|
7540
|
-
var getTickers = async (connector, include = "", exclude = "", limit, chunk) => {
|
|
8208
|
+
var getTickers = async (connector, include = "", exclude = "", limit, chunk, query) => {
|
|
7541
8209
|
let tickers;
|
|
7542
8210
|
const excludeTickers = parseSymbolsFromCLI(exclude);
|
|
7543
8211
|
if (include) {
|
|
7544
8212
|
tickers = parseSymbolsFromCLI(include);
|
|
7545
8213
|
} else {
|
|
7546
|
-
tickers = await scanner(connector, limit);
|
|
8214
|
+
tickers = await scanner(connector, limit, query);
|
|
7547
8215
|
}
|
|
7548
8216
|
if (chunk) {
|
|
7549
8217
|
const [currentChunk, chunksCount] = chunk.split("/").map((c) => parseInt(c));
|
|
@@ -7603,7 +8271,79 @@ var sendToAI = async (signals, userName = "root") => {
|
|
|
7603
8271
|
});
|
|
7604
8272
|
import_logger5.logger.info("");
|
|
7605
8273
|
};
|
|
8274
|
+
var formatRuntimeCloseNotification = (event, _userName = event.userName ?? "root") => {
|
|
8275
|
+
const ownership = event.openedByStrategy === event.strategy ? "matched" : `mismatch: opened by ${event.openedByStrategy}`;
|
|
8276
|
+
return [
|
|
8277
|
+
"<b>Strategy self-close</b>",
|
|
8278
|
+
`Symbol: <b>${escapeHtml2(event.symbol)}</b>`,
|
|
8279
|
+
`Strategy: <b>${escapeHtml2(event.strategy)}</b>`,
|
|
8280
|
+
`Direction: <b>${escapeHtml2(event.direction)}</b>`,
|
|
8281
|
+
`Reason: <code>${escapeHtml2(event.code)}</code>`,
|
|
8282
|
+
"",
|
|
8283
|
+
`Opened by journal: <b>${escapeHtml2(event.openedByStrategy)}</b>`,
|
|
8284
|
+
`Ownership: <b>${escapeHtml2(ownership)}</b>`,
|
|
8285
|
+
"",
|
|
8286
|
+
`Entry: <b>${escapeHtml2(formatOptionalNumber(event.entryPrice))}</b> at <code>${escapeHtml2(formatOptionalTimestamp(event.entryTimestamp))}</code>`,
|
|
8287
|
+
`Exit: <b>${escapeHtml2(formatOptionalNumber(event.exitPrice))}</b> at <code>${escapeHtml2(formatOptionalTimestamp(event.exitTimestamp))}</code>`,
|
|
8288
|
+
`Qty: <b>${escapeHtml2(formatOptionalNumber(event.qty))}</b>`,
|
|
8289
|
+
`Closed PnL: <b>${escapeHtml2(formatOptionalNumber(event.closedPnl))}</b>`
|
|
8290
|
+
].join("\n");
|
|
8291
|
+
};
|
|
8292
|
+
var sendRuntimeCloseNotificationsToTG = async (events, userName = "root") => {
|
|
8293
|
+
import_logger5.logger.info(import_chalk.default.yellow("close messages:", events.length));
|
|
8294
|
+
if (!events.length) {
|
|
8295
|
+
import_logger5.logger.info("");
|
|
8296
|
+
return;
|
|
8297
|
+
}
|
|
8298
|
+
const bar = new import_progress.default(
|
|
8299
|
+
":current/:total [:bar][:percent] :eta(s) :symbol",
|
|
8300
|
+
{
|
|
8301
|
+
total: events.length,
|
|
8302
|
+
width: 30
|
|
8303
|
+
}
|
|
8304
|
+
);
|
|
8305
|
+
await (0, import_async3.runWithConcurrency)(events, 1, async (event) => {
|
|
8306
|
+
try {
|
|
8307
|
+
await sendTextToTG(formatRuntimeCloseNotification(event, userName), {
|
|
8308
|
+
userName
|
|
8309
|
+
});
|
|
8310
|
+
} catch (err) {
|
|
8311
|
+
import_logger5.logger.error(
|
|
8312
|
+
"Failed close notification: %s %s",
|
|
8313
|
+
event.symbol,
|
|
8314
|
+
err?.message || String(err)
|
|
8315
|
+
);
|
|
8316
|
+
} finally {
|
|
8317
|
+
bar.tick(1, { symbol: import_chalk.default.gray(event.symbol) });
|
|
8318
|
+
}
|
|
8319
|
+
});
|
|
8320
|
+
import_logger5.logger.info("");
|
|
8321
|
+
};
|
|
7606
8322
|
var sendToTG = async (signals, imgInterval, userName = "root") => {
|
|
8323
|
+
const strategyConfigCache = /* @__PURE__ */ new Map();
|
|
8324
|
+
const resolveStrategyConfig = async (strategyName) => {
|
|
8325
|
+
if (strategyConfigCache.has(strategyName)) {
|
|
8326
|
+
return strategyConfigCache.get(strategyName);
|
|
8327
|
+
}
|
|
8328
|
+
const config = await (0, import_redis3.getData)(
|
|
8329
|
+
`users:${userName}:strategies:${strategyName}:config`,
|
|
8330
|
+
null
|
|
8331
|
+
);
|
|
8332
|
+
strategyConfigCache.set(strategyName, config);
|
|
8333
|
+
return config;
|
|
8334
|
+
};
|
|
8335
|
+
const resolveDecision = (analysis, direction, minQuality = 4) => {
|
|
8336
|
+
if (analysis?.needRetest === true) {
|
|
8337
|
+
return "rejected";
|
|
8338
|
+
}
|
|
8339
|
+
const quality = Number(analysis?.quality);
|
|
8340
|
+
if (!Number.isFinite(quality)) {
|
|
8341
|
+
return "rejected";
|
|
8342
|
+
}
|
|
8343
|
+
const normalized = Math.round(quality);
|
|
8344
|
+
const resolvedQuality = analysis?.direction === direction ? normalized : 0;
|
|
8345
|
+
return resolvedQuality >= minQuality ? "approved" : "rejected";
|
|
8346
|
+
};
|
|
7607
8347
|
const deliverableSignals = signals.filter(
|
|
7608
8348
|
(signal) => signal.orderStatus !== "skipped" && signal.orderStatus !== "canceled"
|
|
7609
8349
|
);
|
|
@@ -7621,17 +8361,56 @@ var sendToTG = async (signals, imgInterval, userName = "root") => {
|
|
|
7621
8361
|
);
|
|
7622
8362
|
await (0, import_async3.runWithConcurrency)(deliverableSignals, 1, async (signal) => {
|
|
7623
8363
|
try {
|
|
7624
|
-
|
|
8364
|
+
let analysis = await (0, import_redis3.getData)(
|
|
7625
8365
|
import_redis3.redisKeys.analysis(signal.symbol, signal.signalId),
|
|
7626
8366
|
null
|
|
7627
8367
|
);
|
|
8368
|
+
let shouldSendSignalAnalysis = analysis && typeof analysis === "object" && Object.keys(analysis).length > 0;
|
|
8369
|
+
const strategyConfig = await resolveStrategyConfig(signal.strategy);
|
|
8370
|
+
const aiMode = strategyConfig?.AI_MODE;
|
|
8371
|
+
const gateAnalysis = signal.aiAnalysis;
|
|
8372
|
+
if (aiMode === "gate" && gateAnalysis) {
|
|
8373
|
+
const minQuality = Number(strategyConfig?.MIN_AI_QUALITY) || 4;
|
|
8374
|
+
const gateDecision = resolveDecision(
|
|
8375
|
+
gateAnalysis,
|
|
8376
|
+
signal.direction,
|
|
8377
|
+
minQuality
|
|
8378
|
+
);
|
|
8379
|
+
try {
|
|
8380
|
+
const llmAnalysis = await askAI(signal, { userName });
|
|
8381
|
+
const llmDecision = resolveDecision(
|
|
8382
|
+
llmAnalysis,
|
|
8383
|
+
signal.direction,
|
|
8384
|
+
minQuality
|
|
8385
|
+
);
|
|
8386
|
+
analysis = {
|
|
8387
|
+
...llmAnalysis ?? {},
|
|
8388
|
+
gateAnalysis,
|
|
8389
|
+
gateDecision,
|
|
8390
|
+
llmDecision,
|
|
8391
|
+
gateContradictsLlm: gateDecision !== llmDecision
|
|
8392
|
+
};
|
|
8393
|
+
shouldSendSignalAnalysis = true;
|
|
8394
|
+
} catch (error) {
|
|
8395
|
+
import_logger5.logger.error(
|
|
8396
|
+
"LLM commentary failed: %s (%s)",
|
|
8397
|
+
signal.symbol,
|
|
8398
|
+
error?.message || String(error)
|
|
8399
|
+
);
|
|
8400
|
+
analysis = {
|
|
8401
|
+
gateAnalysis,
|
|
8402
|
+
gateDecision
|
|
8403
|
+
};
|
|
8404
|
+
shouldSendSignalAnalysis = false;
|
|
8405
|
+
}
|
|
8406
|
+
}
|
|
7628
8407
|
await sendSignal(signal, imgInterval, analysis, { userName });
|
|
7629
|
-
if (analysis && typeof analysis === "object" && Object.keys(analysis).length > 0) {
|
|
8408
|
+
if (shouldSendSignalAnalysis && analysis && typeof analysis === "object" && Object.keys(analysis).length > 0) {
|
|
7630
8409
|
await sendSignalAnalysis(signal, analysis, { userName });
|
|
7631
8410
|
}
|
|
7632
8411
|
} catch (err) {
|
|
7633
8412
|
import_logger5.logger.error(
|
|
7634
|
-
"
|
|
8413
|
+
"Signal notification failed: %s (%s)",
|
|
7635
8414
|
signal.symbol,
|
|
7636
8415
|
err?.message || String(err)
|
|
7637
8416
|
);
|
|
@@ -7646,9 +8425,12 @@ var sendToTG = async (signals, imgInterval, userName = "root") => {
|
|
|
7646
8425
|
cleanFiles,
|
|
7647
8426
|
cleanRedis,
|
|
7648
8427
|
drawStatInCLI,
|
|
8428
|
+
formatRuntimeCloseNotification,
|
|
7649
8429
|
getTickers,
|
|
7650
8430
|
loadTradejsConfig,
|
|
7651
8431
|
makeScreenshots,
|
|
8432
|
+
sendDocumentToTG,
|
|
8433
|
+
sendRuntimeCloseNotificationsToTG,
|
|
7652
8434
|
sendTextToTG,
|
|
7653
8435
|
sendToAI,
|
|
7654
8436
|
sendToTG,
|