@gbozee/ultimate 0.0.2-95 → 0.0.2-98
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/frontend-index.d.ts +68 -1
- package/dist/frontend-index.js +81 -6
- package/dist/index.cjs +124 -8
- package/dist/index.d.ts +85 -1
- package/dist/index.js +124 -8
- package/dist/mcp-server.cjs +123 -8
- package/dist/mcp-server.js +123 -8
- package/package.json +1 -1
package/dist/frontend-index.d.ts
CHANGED
|
@@ -411,17 +411,28 @@ export declare function computeProfitDetail(payload: {
|
|
|
411
411
|
max_reward_factor: number;
|
|
412
412
|
risk: number;
|
|
413
413
|
};
|
|
414
|
-
reduce_position
|
|
414
|
+
reduce_position?: {
|
|
415
415
|
kind: "long" | "short";
|
|
416
416
|
entry: number;
|
|
417
417
|
quantity: number;
|
|
418
418
|
avg_qty: number;
|
|
419
419
|
avg_price: number;
|
|
420
420
|
};
|
|
421
|
+
reverse_position?: {
|
|
422
|
+
kind: "long" | "short";
|
|
423
|
+
avg_qty: number;
|
|
424
|
+
avg_price: number;
|
|
425
|
+
stop_loss: {
|
|
426
|
+
price: number;
|
|
427
|
+
quantity: number;
|
|
428
|
+
};
|
|
429
|
+
};
|
|
421
430
|
price_places?: string;
|
|
422
431
|
decimal_places?: string;
|
|
423
432
|
}): {
|
|
424
433
|
pnl: number;
|
|
434
|
+
loss: number;
|
|
435
|
+
original_pnl: number;
|
|
425
436
|
reward_factor: number;
|
|
426
437
|
profit_percent: number;
|
|
427
438
|
kind: "long" | "short";
|
|
@@ -430,10 +441,35 @@ export declare function computeProfitDetail(payload: {
|
|
|
430
441
|
price_places: string;
|
|
431
442
|
decimal_places: string;
|
|
432
443
|
};
|
|
444
|
+
export declare function generateGapTp(payload: {
|
|
445
|
+
long: {
|
|
446
|
+
entry: number;
|
|
447
|
+
quantity: number;
|
|
448
|
+
};
|
|
449
|
+
short: {
|
|
450
|
+
entry: number;
|
|
451
|
+
quantity: number;
|
|
452
|
+
};
|
|
453
|
+
factor?: number;
|
|
454
|
+
price_places?: string;
|
|
455
|
+
decimal_places?: string;
|
|
456
|
+
}): {
|
|
457
|
+
long_tp: number;
|
|
458
|
+
short_tp: number;
|
|
459
|
+
short_to_reduce: number;
|
|
460
|
+
long_to_reduce: number;
|
|
461
|
+
to_sell: {
|
|
462
|
+
short: number;
|
|
463
|
+
long: number;
|
|
464
|
+
};
|
|
465
|
+
gap: number;
|
|
466
|
+
gap_loss: number;
|
|
467
|
+
};
|
|
433
468
|
export type StrategyPosition = {
|
|
434
469
|
entry: number;
|
|
435
470
|
quantity: number;
|
|
436
471
|
avg_price?: number;
|
|
472
|
+
avg_qty?: number;
|
|
437
473
|
};
|
|
438
474
|
export type GapCloserResult = {
|
|
439
475
|
avg_entry: number;
|
|
@@ -615,6 +651,37 @@ export declare class Strategy {
|
|
|
615
651
|
rr?: number;
|
|
616
652
|
max_size?: number;
|
|
617
653
|
};
|
|
654
|
+
identifyGapConfig(payload: {
|
|
655
|
+
factor?: number;
|
|
656
|
+
}): {
|
|
657
|
+
long_tp: number;
|
|
658
|
+
short_tp: number;
|
|
659
|
+
short_to_reduce: number;
|
|
660
|
+
long_to_reduce: number;
|
|
661
|
+
to_sell: {
|
|
662
|
+
short: number;
|
|
663
|
+
long: number;
|
|
664
|
+
};
|
|
665
|
+
gap: number;
|
|
666
|
+
gap_loss: number;
|
|
667
|
+
};
|
|
668
|
+
analyzeProfit(payload: {
|
|
669
|
+
reward_factor?: number;
|
|
670
|
+
max_reward_factor: number;
|
|
671
|
+
risk: number;
|
|
672
|
+
kind: "long" | "short";
|
|
673
|
+
}): {
|
|
674
|
+
pnl: number;
|
|
675
|
+
loss: number;
|
|
676
|
+
original_pnl: number;
|
|
677
|
+
reward_factor: number;
|
|
678
|
+
profit_percent: number;
|
|
679
|
+
kind: "long" | "short";
|
|
680
|
+
sell_price: number;
|
|
681
|
+
quantity: number;
|
|
682
|
+
price_places: string;
|
|
683
|
+
decimal_places: string;
|
|
684
|
+
};
|
|
618
685
|
}
|
|
619
686
|
|
|
620
687
|
export {};
|
package/dist/frontend-index.js
CHANGED
|
@@ -1767,7 +1767,8 @@ function computeProfitDetail(payload) {
|
|
|
1767
1767
|
strategy,
|
|
1768
1768
|
price_places = "%.1f",
|
|
1769
1769
|
reduce_position,
|
|
1770
|
-
decimal_places
|
|
1770
|
+
decimal_places,
|
|
1771
|
+
reverse_position
|
|
1771
1772
|
} = payload;
|
|
1772
1773
|
let reward_factor = strategy.reward_factor;
|
|
1773
1774
|
let risk = strategy.risk;
|
|
@@ -1775,7 +1776,7 @@ function computeProfitDetail(payload) {
|
|
|
1775
1776
|
reward_factor = strategy.reward_factor;
|
|
1776
1777
|
}
|
|
1777
1778
|
if (focus_position.avg_qty >= focus_position.quantity && strategy.max_reward_factor) {
|
|
1778
|
-
reward_factor = focus_position.quantity * strategy.max_reward_factor / focus_position.avg_qty;
|
|
1779
|
+
reward_factor = to_f(focus_position.quantity * strategy.max_reward_factor / focus_position.avg_qty, "%.4f");
|
|
1779
1780
|
} else {
|
|
1780
1781
|
reward_factor = strategy.reward_factor;
|
|
1781
1782
|
}
|
|
@@ -1784,11 +1785,23 @@ function computeProfitDetail(payload) {
|
|
|
1784
1785
|
const pnl = to_f(focus_position.entry * focus_position.quantity * profit_percent / 100, "%.2f");
|
|
1785
1786
|
const diff = pnl / focus_position.quantity;
|
|
1786
1787
|
const sell_price = to_f(focus_position.kind === "long" ? focus_position.entry + diff : focus_position.entry - diff, price_places);
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1788
|
+
let loss = 0;
|
|
1789
|
+
let expected_loss = 0;
|
|
1790
|
+
let quantity = 0;
|
|
1791
|
+
let new_pnl = pnl;
|
|
1792
|
+
if (reduce_position) {
|
|
1793
|
+
loss = Math.abs(reduce_position.entry - sell_price) * reduce_position.quantity;
|
|
1794
|
+
const ratio = pnl / loss;
|
|
1795
|
+
quantity = to_f(reduce_position.quantity * ratio, decimal_places);
|
|
1796
|
+
}
|
|
1797
|
+
if (reverse_position) {
|
|
1798
|
+
expected_loss = Math.abs(reverse_position.avg_price - sell_price) * reverse_position.avg_qty;
|
|
1799
|
+
new_pnl = to_f(pnl - expected_loss, "%.2f");
|
|
1800
|
+
}
|
|
1790
1801
|
return {
|
|
1791
|
-
pnl,
|
|
1802
|
+
pnl: new_pnl,
|
|
1803
|
+
loss: to_f(expected_loss, "%.2f"),
|
|
1804
|
+
original_pnl: pnl,
|
|
1792
1805
|
reward_factor,
|
|
1793
1806
|
profit_percent,
|
|
1794
1807
|
kind: focus_position.kind,
|
|
@@ -1798,6 +1811,38 @@ function computeProfitDetail(payload) {
|
|
|
1798
1811
|
decimal_places
|
|
1799
1812
|
};
|
|
1800
1813
|
}
|
|
1814
|
+
function generateGapTp(payload) {
|
|
1815
|
+
const {
|
|
1816
|
+
long,
|
|
1817
|
+
short,
|
|
1818
|
+
factor = 1,
|
|
1819
|
+
price_places = "%.1f",
|
|
1820
|
+
decimal_places = "%.3f"
|
|
1821
|
+
} = payload;
|
|
1822
|
+
const gap = Math.abs(long.entry - short.entry);
|
|
1823
|
+
const max_quantity = Math.max(long.quantity, short.quantity);
|
|
1824
|
+
const gapLoss = gap * max_quantity;
|
|
1825
|
+
const longPercent = gapLoss * factor / (short.entry * short.quantity);
|
|
1826
|
+
const shortPercent = gapLoss * factor / (long.entry * long.quantity);
|
|
1827
|
+
const longTp = to_f((1 + longPercent) * long.entry, price_places);
|
|
1828
|
+
const shortTp = to_f((1 + shortPercent) ** -1 * short.entry, price_places);
|
|
1829
|
+
const shortToReduce = to_f(Math.abs(longTp - long.entry) * long.quantity, "%.1f");
|
|
1830
|
+
const longToReduce = to_f(Math.abs(shortTp - short.entry) * short.quantity, "%.1f");
|
|
1831
|
+
const short_quantity_to_sell = determine_amount_to_sell2(short.entry, short.quantity, longTp, shortToReduce, "short", decimal_places);
|
|
1832
|
+
const long_quantity_to_sell = determine_amount_to_sell2(long.entry, long.quantity, shortTp, longToReduce, "long", decimal_places);
|
|
1833
|
+
return {
|
|
1834
|
+
long_tp: longTp,
|
|
1835
|
+
short_tp: shortTp,
|
|
1836
|
+
short_to_reduce: shortToReduce,
|
|
1837
|
+
long_to_reduce: longToReduce,
|
|
1838
|
+
to_sell: {
|
|
1839
|
+
short: short_quantity_to_sell,
|
|
1840
|
+
long: long_quantity_to_sell
|
|
1841
|
+
},
|
|
1842
|
+
gap: to_f(gap, price_places),
|
|
1843
|
+
gap_loss: to_f(gapLoss, "%.2f")
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1801
1846
|
// src/helpers/strategy.ts
|
|
1802
1847
|
class Strategy {
|
|
1803
1848
|
position;
|
|
@@ -2199,6 +2244,35 @@ class Strategy {
|
|
|
2199
2244
|
app_config.stop = this.to_f(app_config.stop);
|
|
2200
2245
|
return { ...app_config, avg, loss: -expected_loss, profit_percent };
|
|
2201
2246
|
}
|
|
2247
|
+
identifyGapConfig(payload) {
|
|
2248
|
+
const { factor = 1 } = payload;
|
|
2249
|
+
return generateGapTp({
|
|
2250
|
+
long: this.position.long,
|
|
2251
|
+
short: this.position.short,
|
|
2252
|
+
factor,
|
|
2253
|
+
decimal_places: this.config.global_config.decimal_places,
|
|
2254
|
+
price_places: this.config.global_config.price_places
|
|
2255
|
+
});
|
|
2256
|
+
}
|
|
2257
|
+
analyzeProfit(payload) {
|
|
2258
|
+
const { reward_factor = 1, max_reward_factor, risk, kind } = payload;
|
|
2259
|
+
const focus_position = this.position[kind];
|
|
2260
|
+
const result = computeProfitDetail({
|
|
2261
|
+
focus_position: {
|
|
2262
|
+
kind,
|
|
2263
|
+
entry: focus_position.entry,
|
|
2264
|
+
quantity: focus_position.quantity,
|
|
2265
|
+
avg_price: focus_position.avg_price,
|
|
2266
|
+
avg_qty: focus_position.avg_qty
|
|
2267
|
+
},
|
|
2268
|
+
strategy: {
|
|
2269
|
+
reward_factor,
|
|
2270
|
+
max_reward_factor,
|
|
2271
|
+
risk
|
|
2272
|
+
}
|
|
2273
|
+
});
|
|
2274
|
+
return result;
|
|
2275
|
+
}
|
|
2202
2276
|
}
|
|
2203
2277
|
export {
|
|
2204
2278
|
to_f,
|
|
@@ -2217,6 +2291,7 @@ export {
|
|
|
2217
2291
|
getDecimalPlaces,
|
|
2218
2292
|
generate_config_params,
|
|
2219
2293
|
generateOptimumAppConfig,
|
|
2294
|
+
generateGapTp,
|
|
2220
2295
|
formatPrice,
|
|
2221
2296
|
fibonacci_analysis,
|
|
2222
2297
|
extractValue,
|
package/dist/index.cjs
CHANGED
|
@@ -41896,6 +41896,7 @@ __export(exports_src, {
|
|
|
41896
41896
|
getOptimumStopAndRisk: () => getOptimumStopAndRisk,
|
|
41897
41897
|
generate_config_params: () => generate_config_params,
|
|
41898
41898
|
generateOptimumAppConfig: () => generateOptimumAppConfig,
|
|
41899
|
+
generateGapTp: () => generateGapTp,
|
|
41899
41900
|
exchange_account: () => exports_exchange_account,
|
|
41900
41901
|
determine_break_even_price: () => determine_break_even_price,
|
|
41901
41902
|
determine_average_entry_and_size: () => determine_average_entry_and_size,
|
|
@@ -52330,6 +52331,12 @@ function to_f2(value2, places = "%.1f") {
|
|
|
52330
52331
|
const formattedValue = places.replace("%.", "").replace("f", "");
|
|
52331
52332
|
return parseFloat(v.toFixed(parseInt(formattedValue)));
|
|
52332
52333
|
}
|
|
52334
|
+
function determine_amount_to_sell2(entry, quantity, sell_price, pnl, kind, places = "%.3f") {
|
|
52335
|
+
const _pnl = determine_pnl2(entry, sell_price, quantity, kind);
|
|
52336
|
+
const ratio = pnl / to_f2(Math.abs(_pnl), places);
|
|
52337
|
+
quantity = quantity * ratio;
|
|
52338
|
+
return to_f2(quantity, places);
|
|
52339
|
+
}
|
|
52333
52340
|
function determine_position_size2({
|
|
52334
52341
|
entry,
|
|
52335
52342
|
stop,
|
|
@@ -53847,7 +53854,8 @@ function computeProfitDetail(payload) {
|
|
|
53847
53854
|
strategy,
|
|
53848
53855
|
price_places = "%.1f",
|
|
53849
53856
|
reduce_position,
|
|
53850
|
-
decimal_places
|
|
53857
|
+
decimal_places,
|
|
53858
|
+
reverse_position
|
|
53851
53859
|
} = payload;
|
|
53852
53860
|
let reward_factor = strategy.reward_factor;
|
|
53853
53861
|
let risk = strategy.risk;
|
|
@@ -53855,7 +53863,7 @@ function computeProfitDetail(payload) {
|
|
|
53855
53863
|
reward_factor = strategy.reward_factor;
|
|
53856
53864
|
}
|
|
53857
53865
|
if (focus_position.avg_qty >= focus_position.quantity && strategy.max_reward_factor) {
|
|
53858
|
-
reward_factor = focus_position.quantity * strategy.max_reward_factor / focus_position.avg_qty;
|
|
53866
|
+
reward_factor = to_f2(focus_position.quantity * strategy.max_reward_factor / focus_position.avg_qty, "%.4f");
|
|
53859
53867
|
} else {
|
|
53860
53868
|
reward_factor = strategy.reward_factor;
|
|
53861
53869
|
}
|
|
@@ -53864,11 +53872,23 @@ function computeProfitDetail(payload) {
|
|
|
53864
53872
|
const pnl = to_f2(focus_position.entry * focus_position.quantity * profit_percent / 100, "%.2f");
|
|
53865
53873
|
const diff = pnl / focus_position.quantity;
|
|
53866
53874
|
const sell_price = to_f2(focus_position.kind === "long" ? focus_position.entry + diff : focus_position.entry - diff, price_places);
|
|
53867
|
-
|
|
53868
|
-
|
|
53869
|
-
|
|
53875
|
+
let loss = 0;
|
|
53876
|
+
let expected_loss = 0;
|
|
53877
|
+
let quantity = 0;
|
|
53878
|
+
let new_pnl = pnl;
|
|
53879
|
+
if (reduce_position) {
|
|
53880
|
+
loss = Math.abs(reduce_position.entry - sell_price) * reduce_position.quantity;
|
|
53881
|
+
const ratio = pnl / loss;
|
|
53882
|
+
quantity = to_f2(reduce_position.quantity * ratio, decimal_places);
|
|
53883
|
+
}
|
|
53884
|
+
if (reverse_position) {
|
|
53885
|
+
expected_loss = Math.abs(reverse_position.avg_price - sell_price) * reverse_position.avg_qty;
|
|
53886
|
+
new_pnl = to_f2(pnl - expected_loss, "%.2f");
|
|
53887
|
+
}
|
|
53870
53888
|
return {
|
|
53871
|
-
pnl,
|
|
53889
|
+
pnl: new_pnl,
|
|
53890
|
+
loss: to_f2(expected_loss, "%.2f"),
|
|
53891
|
+
original_pnl: pnl,
|
|
53872
53892
|
reward_factor,
|
|
53873
53893
|
profit_percent,
|
|
53874
53894
|
kind: focus_position.kind,
|
|
@@ -53878,6 +53898,38 @@ function computeProfitDetail(payload) {
|
|
|
53878
53898
|
decimal_places
|
|
53879
53899
|
};
|
|
53880
53900
|
}
|
|
53901
|
+
function generateGapTp(payload) {
|
|
53902
|
+
const {
|
|
53903
|
+
long,
|
|
53904
|
+
short,
|
|
53905
|
+
factor = 1,
|
|
53906
|
+
price_places = "%.1f",
|
|
53907
|
+
decimal_places = "%.3f"
|
|
53908
|
+
} = payload;
|
|
53909
|
+
const gap = Math.abs(long.entry - short.entry);
|
|
53910
|
+
const max_quantity = Math.max(long.quantity, short.quantity);
|
|
53911
|
+
const gapLoss = gap * max_quantity;
|
|
53912
|
+
const longPercent = gapLoss * factor / (short.entry * short.quantity);
|
|
53913
|
+
const shortPercent = gapLoss * factor / (long.entry * long.quantity);
|
|
53914
|
+
const longTp = to_f2((1 + longPercent) * long.entry, price_places);
|
|
53915
|
+
const shortTp = to_f2((1 + shortPercent) ** -1 * short.entry, price_places);
|
|
53916
|
+
const shortToReduce = to_f2(Math.abs(longTp - long.entry) * long.quantity, "%.1f");
|
|
53917
|
+
const longToReduce = to_f2(Math.abs(shortTp - short.entry) * short.quantity, "%.1f");
|
|
53918
|
+
const short_quantity_to_sell = determine_amount_to_sell2(short.entry, short.quantity, longTp, shortToReduce, "short", decimal_places);
|
|
53919
|
+
const long_quantity_to_sell = determine_amount_to_sell2(long.entry, long.quantity, shortTp, longToReduce, "long", decimal_places);
|
|
53920
|
+
return {
|
|
53921
|
+
long_tp: longTp,
|
|
53922
|
+
short_tp: shortTp,
|
|
53923
|
+
short_to_reduce: shortToReduce,
|
|
53924
|
+
long_to_reduce: longToReduce,
|
|
53925
|
+
to_sell: {
|
|
53926
|
+
short: short_quantity_to_sell,
|
|
53927
|
+
long: long_quantity_to_sell
|
|
53928
|
+
},
|
|
53929
|
+
gap: to_f2(gap, price_places),
|
|
53930
|
+
gap_loss: to_f2(gapLoss, "%.2f")
|
|
53931
|
+
};
|
|
53932
|
+
}
|
|
53881
53933
|
|
|
53882
53934
|
// src/helpers/strategy.ts
|
|
53883
53935
|
class Strategy {
|
|
@@ -54280,6 +54332,35 @@ class Strategy {
|
|
|
54280
54332
|
app_config.stop = this.to_f(app_config.stop);
|
|
54281
54333
|
return { ...app_config, avg, loss: -expected_loss, profit_percent };
|
|
54282
54334
|
}
|
|
54335
|
+
identifyGapConfig(payload) {
|
|
54336
|
+
const { factor = 1 } = payload;
|
|
54337
|
+
return generateGapTp({
|
|
54338
|
+
long: this.position.long,
|
|
54339
|
+
short: this.position.short,
|
|
54340
|
+
factor,
|
|
54341
|
+
decimal_places: this.config.global_config.decimal_places,
|
|
54342
|
+
price_places: this.config.global_config.price_places
|
|
54343
|
+
});
|
|
54344
|
+
}
|
|
54345
|
+
analyzeProfit(payload) {
|
|
54346
|
+
const { reward_factor = 1, max_reward_factor, risk, kind } = payload;
|
|
54347
|
+
const focus_position = this.position[kind];
|
|
54348
|
+
const result = computeProfitDetail({
|
|
54349
|
+
focus_position: {
|
|
54350
|
+
kind,
|
|
54351
|
+
entry: focus_position.entry,
|
|
54352
|
+
quantity: focus_position.quantity,
|
|
54353
|
+
avg_price: focus_position.avg_price,
|
|
54354
|
+
avg_qty: focus_position.avg_qty
|
|
54355
|
+
},
|
|
54356
|
+
strategy: {
|
|
54357
|
+
reward_factor,
|
|
54358
|
+
max_reward_factor,
|
|
54359
|
+
risk
|
|
54360
|
+
}
|
|
54361
|
+
});
|
|
54362
|
+
return result;
|
|
54363
|
+
}
|
|
54283
54364
|
}
|
|
54284
54365
|
|
|
54285
54366
|
// src/exchanges/binance.ts
|
|
@@ -58207,12 +58288,14 @@ class ExchangeAccount {
|
|
|
58207
58288
|
long: {
|
|
58208
58289
|
entry: long_position.entry,
|
|
58209
58290
|
quantity: long_position.quantity,
|
|
58210
|
-
avg_price: long_position.avg_price
|
|
58291
|
+
avg_price: long_position.avg_price,
|
|
58292
|
+
avg_qty: long_position.avg_qty
|
|
58211
58293
|
},
|
|
58212
58294
|
short: {
|
|
58213
58295
|
entry: short_position.entry,
|
|
58214
58296
|
quantity: short_position.quantity,
|
|
58215
|
-
avg_price: short_position.avg_price
|
|
58297
|
+
avg_price: short_position.avg_price,
|
|
58298
|
+
avg_qty: short_position.avg_qty
|
|
58216
58299
|
},
|
|
58217
58300
|
config: strategy_config
|
|
58218
58301
|
});
|
|
@@ -59067,6 +59150,31 @@ class ExchangeAccount {
|
|
|
59067
59150
|
}
|
|
59068
59151
|
};
|
|
59069
59152
|
}
|
|
59153
|
+
async generateGapTp(payload) {
|
|
59154
|
+
const { symbol, factor = 1 } = payload;
|
|
59155
|
+
const symbol_config = await this.recomputeSymbolConfig({
|
|
59156
|
+
symbol
|
|
59157
|
+
});
|
|
59158
|
+
const positions = await this.syncAccount({
|
|
59159
|
+
symbol,
|
|
59160
|
+
as_view: true
|
|
59161
|
+
});
|
|
59162
|
+
const long_position = positions.find((k) => k.kind === "long");
|
|
59163
|
+
const short_position = positions.find((k) => k.kind === "short");
|
|
59164
|
+
return generateGapTp({
|
|
59165
|
+
long: {
|
|
59166
|
+
entry: long_position.entry,
|
|
59167
|
+
quantity: long_position.quantity
|
|
59168
|
+
},
|
|
59169
|
+
short: {
|
|
59170
|
+
entry: short_position.entry,
|
|
59171
|
+
quantity: short_position.quantity
|
|
59172
|
+
},
|
|
59173
|
+
factor,
|
|
59174
|
+
price_places: symbol_config.price_places,
|
|
59175
|
+
decimal_places: symbol_config.decimal_places
|
|
59176
|
+
});
|
|
59177
|
+
}
|
|
59070
59178
|
async getSellPriceFromStrategy(payload) {
|
|
59071
59179
|
const { symbol, reduce_position } = payload;
|
|
59072
59180
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
@@ -59080,6 +59188,8 @@ class ExchangeAccount {
|
|
|
59080
59188
|
if (!focus_position) {
|
|
59081
59189
|
return;
|
|
59082
59190
|
}
|
|
59191
|
+
const reverse_kind = focus_position.kind === "long" ? "short" : "long";
|
|
59192
|
+
const reverse_position = positions.find((k) => k.kind === reverse_kind);
|
|
59083
59193
|
const strategy = focus_position?.expand?.account_strategy;
|
|
59084
59194
|
if (!strategy) {
|
|
59085
59195
|
return;
|
|
@@ -59104,6 +59214,12 @@ class ExchangeAccount {
|
|
|
59104
59214
|
avg_price: reduce_position.avg_price,
|
|
59105
59215
|
avg_qty: reduce_position.avg_qty
|
|
59106
59216
|
},
|
|
59217
|
+
reverse_position: {
|
|
59218
|
+
kind: reverse_position.kind,
|
|
59219
|
+
avg_price: reverse_position.avg_price,
|
|
59220
|
+
avg_qty: reverse_position.avg_qty,
|
|
59221
|
+
stop_loss: reverse_position.stop_loss
|
|
59222
|
+
},
|
|
59107
59223
|
price_places: symbol_config.price_places,
|
|
59108
59224
|
decimal_places: symbol_config.decimal_places
|
|
59109
59225
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -565,6 +565,7 @@ export type StrategyPosition = {
|
|
|
565
565
|
entry: number;
|
|
566
566
|
quantity: number;
|
|
567
567
|
avg_price?: number;
|
|
568
|
+
avg_qty?: number;
|
|
568
569
|
};
|
|
569
570
|
export type GapCloserResult = {
|
|
570
571
|
avg_entry: number;
|
|
@@ -746,6 +747,37 @@ export declare class Strategy {
|
|
|
746
747
|
rr?: number;
|
|
747
748
|
max_size?: number;
|
|
748
749
|
};
|
|
750
|
+
identifyGapConfig(payload: {
|
|
751
|
+
factor?: number;
|
|
752
|
+
}): {
|
|
753
|
+
long_tp: number;
|
|
754
|
+
short_tp: number;
|
|
755
|
+
short_to_reduce: number;
|
|
756
|
+
long_to_reduce: number;
|
|
757
|
+
to_sell: {
|
|
758
|
+
short: number;
|
|
759
|
+
long: number;
|
|
760
|
+
};
|
|
761
|
+
gap: number;
|
|
762
|
+
gap_loss: number;
|
|
763
|
+
};
|
|
764
|
+
analyzeProfit(payload: {
|
|
765
|
+
reward_factor?: number;
|
|
766
|
+
max_reward_factor: number;
|
|
767
|
+
risk: number;
|
|
768
|
+
kind: "long" | "short";
|
|
769
|
+
}): {
|
|
770
|
+
pnl: number;
|
|
771
|
+
loss: number;
|
|
772
|
+
original_pnl: number;
|
|
773
|
+
reward_factor: number;
|
|
774
|
+
profit_percent: number;
|
|
775
|
+
kind: "long" | "short";
|
|
776
|
+
sell_price: number;
|
|
777
|
+
quantity: number;
|
|
778
|
+
price_places: string;
|
|
779
|
+
decimal_places: string;
|
|
780
|
+
};
|
|
749
781
|
}
|
|
750
782
|
export type SignalConfigType = {
|
|
751
783
|
focus: number;
|
|
@@ -1068,17 +1100,28 @@ export declare function computeProfitDetail(payload: {
|
|
|
1068
1100
|
max_reward_factor: number;
|
|
1069
1101
|
risk: number;
|
|
1070
1102
|
};
|
|
1071
|
-
reduce_position
|
|
1103
|
+
reduce_position?: {
|
|
1072
1104
|
kind: "long" | "short";
|
|
1073
1105
|
entry: number;
|
|
1074
1106
|
quantity: number;
|
|
1075
1107
|
avg_qty: number;
|
|
1076
1108
|
avg_price: number;
|
|
1077
1109
|
};
|
|
1110
|
+
reverse_position?: {
|
|
1111
|
+
kind: "long" | "short";
|
|
1112
|
+
avg_qty: number;
|
|
1113
|
+
avg_price: number;
|
|
1114
|
+
stop_loss: {
|
|
1115
|
+
price: number;
|
|
1116
|
+
quantity: number;
|
|
1117
|
+
};
|
|
1118
|
+
};
|
|
1078
1119
|
price_places?: string;
|
|
1079
1120
|
decimal_places?: string;
|
|
1080
1121
|
}): {
|
|
1081
1122
|
pnl: number;
|
|
1123
|
+
loss: number;
|
|
1124
|
+
original_pnl: number;
|
|
1082
1125
|
reward_factor: number;
|
|
1083
1126
|
profit_percent: number;
|
|
1084
1127
|
kind: "long" | "short";
|
|
@@ -1087,6 +1130,30 @@ export declare function computeProfitDetail(payload: {
|
|
|
1087
1130
|
price_places: string;
|
|
1088
1131
|
decimal_places: string;
|
|
1089
1132
|
};
|
|
1133
|
+
export declare function generateGapTp(payload: {
|
|
1134
|
+
long: {
|
|
1135
|
+
entry: number;
|
|
1136
|
+
quantity: number;
|
|
1137
|
+
};
|
|
1138
|
+
short: {
|
|
1139
|
+
entry: number;
|
|
1140
|
+
quantity: number;
|
|
1141
|
+
};
|
|
1142
|
+
factor?: number;
|
|
1143
|
+
price_places?: string;
|
|
1144
|
+
decimal_places?: string;
|
|
1145
|
+
}): {
|
|
1146
|
+
long_tp: number;
|
|
1147
|
+
short_tp: number;
|
|
1148
|
+
short_to_reduce: number;
|
|
1149
|
+
long_to_reduce: number;
|
|
1150
|
+
to_sell: {
|
|
1151
|
+
short: number;
|
|
1152
|
+
long: number;
|
|
1153
|
+
};
|
|
1154
|
+
gap: number;
|
|
1155
|
+
gap_loss: number;
|
|
1156
|
+
};
|
|
1090
1157
|
declare class ExchangePosition {
|
|
1091
1158
|
exchange: BaseExchange;
|
|
1092
1159
|
exchange_account: ExchangeAccount$1;
|
|
@@ -1845,11 +1912,28 @@ declare class ExchangeAccount$1 {
|
|
|
1845
1912
|
pnl: number;
|
|
1846
1913
|
};
|
|
1847
1914
|
}>;
|
|
1915
|
+
generateGapTp(payload: {
|
|
1916
|
+
symbol: string;
|
|
1917
|
+
factor?: number;
|
|
1918
|
+
}): Promise<{
|
|
1919
|
+
long_tp: number;
|
|
1920
|
+
short_tp: number;
|
|
1921
|
+
short_to_reduce: number;
|
|
1922
|
+
long_to_reduce: number;
|
|
1923
|
+
to_sell: {
|
|
1924
|
+
short: number;
|
|
1925
|
+
long: number;
|
|
1926
|
+
};
|
|
1927
|
+
gap: number;
|
|
1928
|
+
gap_loss: number;
|
|
1929
|
+
}>;
|
|
1848
1930
|
getSellPriceFromStrategy(payload: {
|
|
1849
1931
|
symbol: string;
|
|
1850
1932
|
reduce_position: PositionsView;
|
|
1851
1933
|
}): Promise<{
|
|
1852
1934
|
pnl: number;
|
|
1935
|
+
loss: number;
|
|
1936
|
+
original_pnl: number;
|
|
1853
1937
|
reward_factor: number;
|
|
1854
1938
|
profit_percent: number;
|
|
1855
1939
|
kind: "long" | "short";
|
package/dist/index.js
CHANGED
|
@@ -52284,6 +52284,12 @@ function to_f2(value2, places = "%.1f") {
|
|
|
52284
52284
|
const formattedValue = places.replace("%.", "").replace("f", "");
|
|
52285
52285
|
return parseFloat(v.toFixed(parseInt(formattedValue)));
|
|
52286
52286
|
}
|
|
52287
|
+
function determine_amount_to_sell2(entry, quantity, sell_price, pnl, kind, places = "%.3f") {
|
|
52288
|
+
const _pnl = determine_pnl2(entry, sell_price, quantity, kind);
|
|
52289
|
+
const ratio = pnl / to_f2(Math.abs(_pnl), places);
|
|
52290
|
+
quantity = quantity * ratio;
|
|
52291
|
+
return to_f2(quantity, places);
|
|
52292
|
+
}
|
|
52287
52293
|
function determine_position_size2({
|
|
52288
52294
|
entry,
|
|
52289
52295
|
stop,
|
|
@@ -53801,7 +53807,8 @@ function computeProfitDetail(payload) {
|
|
|
53801
53807
|
strategy,
|
|
53802
53808
|
price_places = "%.1f",
|
|
53803
53809
|
reduce_position,
|
|
53804
|
-
decimal_places
|
|
53810
|
+
decimal_places,
|
|
53811
|
+
reverse_position
|
|
53805
53812
|
} = payload;
|
|
53806
53813
|
let reward_factor = strategy.reward_factor;
|
|
53807
53814
|
let risk = strategy.risk;
|
|
@@ -53809,7 +53816,7 @@ function computeProfitDetail(payload) {
|
|
|
53809
53816
|
reward_factor = strategy.reward_factor;
|
|
53810
53817
|
}
|
|
53811
53818
|
if (focus_position.avg_qty >= focus_position.quantity && strategy.max_reward_factor) {
|
|
53812
|
-
reward_factor = focus_position.quantity * strategy.max_reward_factor / focus_position.avg_qty;
|
|
53819
|
+
reward_factor = to_f2(focus_position.quantity * strategy.max_reward_factor / focus_position.avg_qty, "%.4f");
|
|
53813
53820
|
} else {
|
|
53814
53821
|
reward_factor = strategy.reward_factor;
|
|
53815
53822
|
}
|
|
@@ -53818,11 +53825,23 @@ function computeProfitDetail(payload) {
|
|
|
53818
53825
|
const pnl = to_f2(focus_position.entry * focus_position.quantity * profit_percent / 100, "%.2f");
|
|
53819
53826
|
const diff = pnl / focus_position.quantity;
|
|
53820
53827
|
const sell_price = to_f2(focus_position.kind === "long" ? focus_position.entry + diff : focus_position.entry - diff, price_places);
|
|
53821
|
-
|
|
53822
|
-
|
|
53823
|
-
|
|
53828
|
+
let loss = 0;
|
|
53829
|
+
let expected_loss = 0;
|
|
53830
|
+
let quantity = 0;
|
|
53831
|
+
let new_pnl = pnl;
|
|
53832
|
+
if (reduce_position) {
|
|
53833
|
+
loss = Math.abs(reduce_position.entry - sell_price) * reduce_position.quantity;
|
|
53834
|
+
const ratio = pnl / loss;
|
|
53835
|
+
quantity = to_f2(reduce_position.quantity * ratio, decimal_places);
|
|
53836
|
+
}
|
|
53837
|
+
if (reverse_position) {
|
|
53838
|
+
expected_loss = Math.abs(reverse_position.avg_price - sell_price) * reverse_position.avg_qty;
|
|
53839
|
+
new_pnl = to_f2(pnl - expected_loss, "%.2f");
|
|
53840
|
+
}
|
|
53824
53841
|
return {
|
|
53825
|
-
pnl,
|
|
53842
|
+
pnl: new_pnl,
|
|
53843
|
+
loss: to_f2(expected_loss, "%.2f"),
|
|
53844
|
+
original_pnl: pnl,
|
|
53826
53845
|
reward_factor,
|
|
53827
53846
|
profit_percent,
|
|
53828
53847
|
kind: focus_position.kind,
|
|
@@ -53832,6 +53851,38 @@ function computeProfitDetail(payload) {
|
|
|
53832
53851
|
decimal_places
|
|
53833
53852
|
};
|
|
53834
53853
|
}
|
|
53854
|
+
function generateGapTp(payload) {
|
|
53855
|
+
const {
|
|
53856
|
+
long,
|
|
53857
|
+
short,
|
|
53858
|
+
factor = 1,
|
|
53859
|
+
price_places = "%.1f",
|
|
53860
|
+
decimal_places = "%.3f"
|
|
53861
|
+
} = payload;
|
|
53862
|
+
const gap = Math.abs(long.entry - short.entry);
|
|
53863
|
+
const max_quantity = Math.max(long.quantity, short.quantity);
|
|
53864
|
+
const gapLoss = gap * max_quantity;
|
|
53865
|
+
const longPercent = gapLoss * factor / (short.entry * short.quantity);
|
|
53866
|
+
const shortPercent = gapLoss * factor / (long.entry * long.quantity);
|
|
53867
|
+
const longTp = to_f2((1 + longPercent) * long.entry, price_places);
|
|
53868
|
+
const shortTp = to_f2((1 + shortPercent) ** -1 * short.entry, price_places);
|
|
53869
|
+
const shortToReduce = to_f2(Math.abs(longTp - long.entry) * long.quantity, "%.1f");
|
|
53870
|
+
const longToReduce = to_f2(Math.abs(shortTp - short.entry) * short.quantity, "%.1f");
|
|
53871
|
+
const short_quantity_to_sell = determine_amount_to_sell2(short.entry, short.quantity, longTp, shortToReduce, "short", decimal_places);
|
|
53872
|
+
const long_quantity_to_sell = determine_amount_to_sell2(long.entry, long.quantity, shortTp, longToReduce, "long", decimal_places);
|
|
53873
|
+
return {
|
|
53874
|
+
long_tp: longTp,
|
|
53875
|
+
short_tp: shortTp,
|
|
53876
|
+
short_to_reduce: shortToReduce,
|
|
53877
|
+
long_to_reduce: longToReduce,
|
|
53878
|
+
to_sell: {
|
|
53879
|
+
short: short_quantity_to_sell,
|
|
53880
|
+
long: long_quantity_to_sell
|
|
53881
|
+
},
|
|
53882
|
+
gap: to_f2(gap, price_places),
|
|
53883
|
+
gap_loss: to_f2(gapLoss, "%.2f")
|
|
53884
|
+
};
|
|
53885
|
+
}
|
|
53835
53886
|
|
|
53836
53887
|
// src/helpers/strategy.ts
|
|
53837
53888
|
class Strategy {
|
|
@@ -54234,6 +54285,35 @@ class Strategy {
|
|
|
54234
54285
|
app_config.stop = this.to_f(app_config.stop);
|
|
54235
54286
|
return { ...app_config, avg, loss: -expected_loss, profit_percent };
|
|
54236
54287
|
}
|
|
54288
|
+
identifyGapConfig(payload) {
|
|
54289
|
+
const { factor = 1 } = payload;
|
|
54290
|
+
return generateGapTp({
|
|
54291
|
+
long: this.position.long,
|
|
54292
|
+
short: this.position.short,
|
|
54293
|
+
factor,
|
|
54294
|
+
decimal_places: this.config.global_config.decimal_places,
|
|
54295
|
+
price_places: this.config.global_config.price_places
|
|
54296
|
+
});
|
|
54297
|
+
}
|
|
54298
|
+
analyzeProfit(payload) {
|
|
54299
|
+
const { reward_factor = 1, max_reward_factor, risk, kind } = payload;
|
|
54300
|
+
const focus_position = this.position[kind];
|
|
54301
|
+
const result = computeProfitDetail({
|
|
54302
|
+
focus_position: {
|
|
54303
|
+
kind,
|
|
54304
|
+
entry: focus_position.entry,
|
|
54305
|
+
quantity: focus_position.quantity,
|
|
54306
|
+
avg_price: focus_position.avg_price,
|
|
54307
|
+
avg_qty: focus_position.avg_qty
|
|
54308
|
+
},
|
|
54309
|
+
strategy: {
|
|
54310
|
+
reward_factor,
|
|
54311
|
+
max_reward_factor,
|
|
54312
|
+
risk
|
|
54313
|
+
}
|
|
54314
|
+
});
|
|
54315
|
+
return result;
|
|
54316
|
+
}
|
|
54237
54317
|
}
|
|
54238
54318
|
|
|
54239
54319
|
// src/exchanges/binance.ts
|
|
@@ -58161,12 +58241,14 @@ class ExchangeAccount {
|
|
|
58161
58241
|
long: {
|
|
58162
58242
|
entry: long_position.entry,
|
|
58163
58243
|
quantity: long_position.quantity,
|
|
58164
|
-
avg_price: long_position.avg_price
|
|
58244
|
+
avg_price: long_position.avg_price,
|
|
58245
|
+
avg_qty: long_position.avg_qty
|
|
58165
58246
|
},
|
|
58166
58247
|
short: {
|
|
58167
58248
|
entry: short_position.entry,
|
|
58168
58249
|
quantity: short_position.quantity,
|
|
58169
|
-
avg_price: short_position.avg_price
|
|
58250
|
+
avg_price: short_position.avg_price,
|
|
58251
|
+
avg_qty: short_position.avg_qty
|
|
58170
58252
|
},
|
|
58171
58253
|
config: strategy_config
|
|
58172
58254
|
});
|
|
@@ -59021,6 +59103,31 @@ class ExchangeAccount {
|
|
|
59021
59103
|
}
|
|
59022
59104
|
};
|
|
59023
59105
|
}
|
|
59106
|
+
async generateGapTp(payload) {
|
|
59107
|
+
const { symbol, factor = 1 } = payload;
|
|
59108
|
+
const symbol_config = await this.recomputeSymbolConfig({
|
|
59109
|
+
symbol
|
|
59110
|
+
});
|
|
59111
|
+
const positions = await this.syncAccount({
|
|
59112
|
+
symbol,
|
|
59113
|
+
as_view: true
|
|
59114
|
+
});
|
|
59115
|
+
const long_position = positions.find((k) => k.kind === "long");
|
|
59116
|
+
const short_position = positions.find((k) => k.kind === "short");
|
|
59117
|
+
return generateGapTp({
|
|
59118
|
+
long: {
|
|
59119
|
+
entry: long_position.entry,
|
|
59120
|
+
quantity: long_position.quantity
|
|
59121
|
+
},
|
|
59122
|
+
short: {
|
|
59123
|
+
entry: short_position.entry,
|
|
59124
|
+
quantity: short_position.quantity
|
|
59125
|
+
},
|
|
59126
|
+
factor,
|
|
59127
|
+
price_places: symbol_config.price_places,
|
|
59128
|
+
decimal_places: symbol_config.decimal_places
|
|
59129
|
+
});
|
|
59130
|
+
}
|
|
59024
59131
|
async getSellPriceFromStrategy(payload) {
|
|
59025
59132
|
const { symbol, reduce_position } = payload;
|
|
59026
59133
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
@@ -59034,6 +59141,8 @@ class ExchangeAccount {
|
|
|
59034
59141
|
if (!focus_position) {
|
|
59035
59142
|
return;
|
|
59036
59143
|
}
|
|
59144
|
+
const reverse_kind = focus_position.kind === "long" ? "short" : "long";
|
|
59145
|
+
const reverse_position = positions.find((k) => k.kind === reverse_kind);
|
|
59037
59146
|
const strategy = focus_position?.expand?.account_strategy;
|
|
59038
59147
|
if (!strategy) {
|
|
59039
59148
|
return;
|
|
@@ -59058,6 +59167,12 @@ class ExchangeAccount {
|
|
|
59058
59167
|
avg_price: reduce_position.avg_price,
|
|
59059
59168
|
avg_qty: reduce_position.avg_qty
|
|
59060
59169
|
},
|
|
59170
|
+
reverse_position: {
|
|
59171
|
+
kind: reverse_position.kind,
|
|
59172
|
+
avg_price: reverse_position.avg_price,
|
|
59173
|
+
avg_qty: reverse_position.avg_qty,
|
|
59174
|
+
stop_loss: reverse_position.stop_loss
|
|
59175
|
+
},
|
|
59061
59176
|
price_places: symbol_config.price_places,
|
|
59062
59177
|
decimal_places: symbol_config.decimal_places
|
|
59063
59178
|
});
|
|
@@ -59518,6 +59633,7 @@ export {
|
|
|
59518
59633
|
getOptimumStopAndRisk,
|
|
59519
59634
|
generate_config_params,
|
|
59520
59635
|
generateOptimumAppConfig,
|
|
59636
|
+
generateGapTp,
|
|
59521
59637
|
exports_exchange_account as exchange_account,
|
|
59522
59638
|
determine_break_even_price,
|
|
59523
59639
|
determine_average_entry_and_size,
|
package/dist/mcp-server.cjs
CHANGED
|
@@ -59033,6 +59033,12 @@ function to_f2(value2, places = "%.1f") {
|
|
|
59033
59033
|
const formattedValue = places.replace("%.", "").replace("f", "");
|
|
59034
59034
|
return parseFloat(v.toFixed(parseInt(formattedValue)));
|
|
59035
59035
|
}
|
|
59036
|
+
function determine_amount_to_sell2(entry, quantity, sell_price, pnl, kind, places = "%.3f") {
|
|
59037
|
+
const _pnl = determine_pnl2(entry, sell_price, quantity, kind);
|
|
59038
|
+
const ratio = pnl / to_f2(Math.abs(_pnl), places);
|
|
59039
|
+
quantity = quantity * ratio;
|
|
59040
|
+
return to_f2(quantity, places);
|
|
59041
|
+
}
|
|
59036
59042
|
function determine_position_size2({
|
|
59037
59043
|
entry,
|
|
59038
59044
|
stop,
|
|
@@ -60537,7 +60543,8 @@ function computeProfitDetail(payload) {
|
|
|
60537
60543
|
strategy,
|
|
60538
60544
|
price_places = "%.1f",
|
|
60539
60545
|
reduce_position,
|
|
60540
|
-
decimal_places
|
|
60546
|
+
decimal_places,
|
|
60547
|
+
reverse_position
|
|
60541
60548
|
} = payload;
|
|
60542
60549
|
let reward_factor = strategy.reward_factor;
|
|
60543
60550
|
let risk = strategy.risk;
|
|
@@ -60545,7 +60552,7 @@ function computeProfitDetail(payload) {
|
|
|
60545
60552
|
reward_factor = strategy.reward_factor;
|
|
60546
60553
|
}
|
|
60547
60554
|
if (focus_position.avg_qty >= focus_position.quantity && strategy.max_reward_factor) {
|
|
60548
|
-
reward_factor = focus_position.quantity * strategy.max_reward_factor / focus_position.avg_qty;
|
|
60555
|
+
reward_factor = to_f2(focus_position.quantity * strategy.max_reward_factor / focus_position.avg_qty, "%.4f");
|
|
60549
60556
|
} else {
|
|
60550
60557
|
reward_factor = strategy.reward_factor;
|
|
60551
60558
|
}
|
|
@@ -60554,11 +60561,23 @@ function computeProfitDetail(payload) {
|
|
|
60554
60561
|
const pnl = to_f2(focus_position.entry * focus_position.quantity * profit_percent / 100, "%.2f");
|
|
60555
60562
|
const diff = pnl / focus_position.quantity;
|
|
60556
60563
|
const sell_price = to_f2(focus_position.kind === "long" ? focus_position.entry + diff : focus_position.entry - diff, price_places);
|
|
60557
|
-
|
|
60558
|
-
|
|
60559
|
-
|
|
60564
|
+
let loss = 0;
|
|
60565
|
+
let expected_loss = 0;
|
|
60566
|
+
let quantity = 0;
|
|
60567
|
+
let new_pnl = pnl;
|
|
60568
|
+
if (reduce_position) {
|
|
60569
|
+
loss = Math.abs(reduce_position.entry - sell_price) * reduce_position.quantity;
|
|
60570
|
+
const ratio = pnl / loss;
|
|
60571
|
+
quantity = to_f2(reduce_position.quantity * ratio, decimal_places);
|
|
60572
|
+
}
|
|
60573
|
+
if (reverse_position) {
|
|
60574
|
+
expected_loss = Math.abs(reverse_position.avg_price - sell_price) * reverse_position.avg_qty;
|
|
60575
|
+
new_pnl = to_f2(pnl - expected_loss, "%.2f");
|
|
60576
|
+
}
|
|
60560
60577
|
return {
|
|
60561
|
-
pnl,
|
|
60578
|
+
pnl: new_pnl,
|
|
60579
|
+
loss: to_f2(expected_loss, "%.2f"),
|
|
60580
|
+
original_pnl: pnl,
|
|
60562
60581
|
reward_factor,
|
|
60563
60582
|
profit_percent,
|
|
60564
60583
|
kind: focus_position.kind,
|
|
@@ -60568,6 +60587,38 @@ function computeProfitDetail(payload) {
|
|
|
60568
60587
|
decimal_places
|
|
60569
60588
|
};
|
|
60570
60589
|
}
|
|
60590
|
+
function generateGapTp(payload) {
|
|
60591
|
+
const {
|
|
60592
|
+
long,
|
|
60593
|
+
short,
|
|
60594
|
+
factor = 1,
|
|
60595
|
+
price_places = "%.1f",
|
|
60596
|
+
decimal_places = "%.3f"
|
|
60597
|
+
} = payload;
|
|
60598
|
+
const gap = Math.abs(long.entry - short.entry);
|
|
60599
|
+
const max_quantity = Math.max(long.quantity, short.quantity);
|
|
60600
|
+
const gapLoss = gap * max_quantity;
|
|
60601
|
+
const longPercent = gapLoss * factor / (short.entry * short.quantity);
|
|
60602
|
+
const shortPercent = gapLoss * factor / (long.entry * long.quantity);
|
|
60603
|
+
const longTp = to_f2((1 + longPercent) * long.entry, price_places);
|
|
60604
|
+
const shortTp = to_f2((1 + shortPercent) ** -1 * short.entry, price_places);
|
|
60605
|
+
const shortToReduce = to_f2(Math.abs(longTp - long.entry) * long.quantity, "%.1f");
|
|
60606
|
+
const longToReduce = to_f2(Math.abs(shortTp - short.entry) * short.quantity, "%.1f");
|
|
60607
|
+
const short_quantity_to_sell = determine_amount_to_sell2(short.entry, short.quantity, longTp, shortToReduce, "short", decimal_places);
|
|
60608
|
+
const long_quantity_to_sell = determine_amount_to_sell2(long.entry, long.quantity, shortTp, longToReduce, "long", decimal_places);
|
|
60609
|
+
return {
|
|
60610
|
+
long_tp: longTp,
|
|
60611
|
+
short_tp: shortTp,
|
|
60612
|
+
short_to_reduce: shortToReduce,
|
|
60613
|
+
long_to_reduce: longToReduce,
|
|
60614
|
+
to_sell: {
|
|
60615
|
+
short: short_quantity_to_sell,
|
|
60616
|
+
long: long_quantity_to_sell
|
|
60617
|
+
},
|
|
60618
|
+
gap: to_f2(gap, price_places),
|
|
60619
|
+
gap_loss: to_f2(gapLoss, "%.2f")
|
|
60620
|
+
};
|
|
60621
|
+
}
|
|
60571
60622
|
|
|
60572
60623
|
// src/helpers/strategy.ts
|
|
60573
60624
|
class Strategy {
|
|
@@ -60970,6 +61021,35 @@ class Strategy {
|
|
|
60970
61021
|
app_config.stop = this.to_f(app_config.stop);
|
|
60971
61022
|
return { ...app_config, avg, loss: -expected_loss, profit_percent };
|
|
60972
61023
|
}
|
|
61024
|
+
identifyGapConfig(payload) {
|
|
61025
|
+
const { factor = 1 } = payload;
|
|
61026
|
+
return generateGapTp({
|
|
61027
|
+
long: this.position.long,
|
|
61028
|
+
short: this.position.short,
|
|
61029
|
+
factor,
|
|
61030
|
+
decimal_places: this.config.global_config.decimal_places,
|
|
61031
|
+
price_places: this.config.global_config.price_places
|
|
61032
|
+
});
|
|
61033
|
+
}
|
|
61034
|
+
analyzeProfit(payload) {
|
|
61035
|
+
const { reward_factor = 1, max_reward_factor, risk, kind } = payload;
|
|
61036
|
+
const focus_position = this.position[kind];
|
|
61037
|
+
const result = computeProfitDetail({
|
|
61038
|
+
focus_position: {
|
|
61039
|
+
kind,
|
|
61040
|
+
entry: focus_position.entry,
|
|
61041
|
+
quantity: focus_position.quantity,
|
|
61042
|
+
avg_price: focus_position.avg_price,
|
|
61043
|
+
avg_qty: focus_position.avg_qty
|
|
61044
|
+
},
|
|
61045
|
+
strategy: {
|
|
61046
|
+
reward_factor,
|
|
61047
|
+
max_reward_factor,
|
|
61048
|
+
risk
|
|
61049
|
+
}
|
|
61050
|
+
});
|
|
61051
|
+
return result;
|
|
61052
|
+
}
|
|
60973
61053
|
}
|
|
60974
61054
|
|
|
60975
61055
|
// src/exchanges/binance.ts
|
|
@@ -64897,12 +64977,14 @@ class ExchangeAccount {
|
|
|
64897
64977
|
long: {
|
|
64898
64978
|
entry: long_position.entry,
|
|
64899
64979
|
quantity: long_position.quantity,
|
|
64900
|
-
avg_price: long_position.avg_price
|
|
64980
|
+
avg_price: long_position.avg_price,
|
|
64981
|
+
avg_qty: long_position.avg_qty
|
|
64901
64982
|
},
|
|
64902
64983
|
short: {
|
|
64903
64984
|
entry: short_position.entry,
|
|
64904
64985
|
quantity: short_position.quantity,
|
|
64905
|
-
avg_price: short_position.avg_price
|
|
64986
|
+
avg_price: short_position.avg_price,
|
|
64987
|
+
avg_qty: short_position.avg_qty
|
|
64906
64988
|
},
|
|
64907
64989
|
config: strategy_config
|
|
64908
64990
|
});
|
|
@@ -65757,6 +65839,31 @@ class ExchangeAccount {
|
|
|
65757
65839
|
}
|
|
65758
65840
|
};
|
|
65759
65841
|
}
|
|
65842
|
+
async generateGapTp(payload) {
|
|
65843
|
+
const { symbol, factor = 1 } = payload;
|
|
65844
|
+
const symbol_config = await this.recomputeSymbolConfig({
|
|
65845
|
+
symbol
|
|
65846
|
+
});
|
|
65847
|
+
const positions = await this.syncAccount({
|
|
65848
|
+
symbol,
|
|
65849
|
+
as_view: true
|
|
65850
|
+
});
|
|
65851
|
+
const long_position = positions.find((k) => k.kind === "long");
|
|
65852
|
+
const short_position = positions.find((k) => k.kind === "short");
|
|
65853
|
+
return generateGapTp({
|
|
65854
|
+
long: {
|
|
65855
|
+
entry: long_position.entry,
|
|
65856
|
+
quantity: long_position.quantity
|
|
65857
|
+
},
|
|
65858
|
+
short: {
|
|
65859
|
+
entry: short_position.entry,
|
|
65860
|
+
quantity: short_position.quantity
|
|
65861
|
+
},
|
|
65862
|
+
factor,
|
|
65863
|
+
price_places: symbol_config.price_places,
|
|
65864
|
+
decimal_places: symbol_config.decimal_places
|
|
65865
|
+
});
|
|
65866
|
+
}
|
|
65760
65867
|
async getSellPriceFromStrategy(payload) {
|
|
65761
65868
|
const { symbol, reduce_position } = payload;
|
|
65762
65869
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
@@ -65770,6 +65877,8 @@ class ExchangeAccount {
|
|
|
65770
65877
|
if (!focus_position) {
|
|
65771
65878
|
return;
|
|
65772
65879
|
}
|
|
65880
|
+
const reverse_kind = focus_position.kind === "long" ? "short" : "long";
|
|
65881
|
+
const reverse_position = positions.find((k) => k.kind === reverse_kind);
|
|
65773
65882
|
const strategy = focus_position?.expand?.account_strategy;
|
|
65774
65883
|
if (!strategy) {
|
|
65775
65884
|
return;
|
|
@@ -65794,6 +65903,12 @@ class ExchangeAccount {
|
|
|
65794
65903
|
avg_price: reduce_position.avg_price,
|
|
65795
65904
|
avg_qty: reduce_position.avg_qty
|
|
65796
65905
|
},
|
|
65906
|
+
reverse_position: {
|
|
65907
|
+
kind: reverse_position.kind,
|
|
65908
|
+
avg_price: reverse_position.avg_price,
|
|
65909
|
+
avg_qty: reverse_position.avg_qty,
|
|
65910
|
+
stop_loss: reverse_position.stop_loss
|
|
65911
|
+
},
|
|
65797
65912
|
price_places: symbol_config.price_places,
|
|
65798
65913
|
decimal_places: symbol_config.decimal_places
|
|
65799
65914
|
});
|
package/dist/mcp-server.js
CHANGED
|
@@ -59010,6 +59010,12 @@ function to_f2(value2, places = "%.1f") {
|
|
|
59010
59010
|
const formattedValue = places.replace("%.", "").replace("f", "");
|
|
59011
59011
|
return parseFloat(v.toFixed(parseInt(formattedValue)));
|
|
59012
59012
|
}
|
|
59013
|
+
function determine_amount_to_sell2(entry, quantity, sell_price, pnl, kind, places = "%.3f") {
|
|
59014
|
+
const _pnl = determine_pnl2(entry, sell_price, quantity, kind);
|
|
59015
|
+
const ratio = pnl / to_f2(Math.abs(_pnl), places);
|
|
59016
|
+
quantity = quantity * ratio;
|
|
59017
|
+
return to_f2(quantity, places);
|
|
59018
|
+
}
|
|
59013
59019
|
function determine_position_size2({
|
|
59014
59020
|
entry,
|
|
59015
59021
|
stop,
|
|
@@ -60514,7 +60520,8 @@ function computeProfitDetail(payload) {
|
|
|
60514
60520
|
strategy,
|
|
60515
60521
|
price_places = "%.1f",
|
|
60516
60522
|
reduce_position,
|
|
60517
|
-
decimal_places
|
|
60523
|
+
decimal_places,
|
|
60524
|
+
reverse_position
|
|
60518
60525
|
} = payload;
|
|
60519
60526
|
let reward_factor = strategy.reward_factor;
|
|
60520
60527
|
let risk = strategy.risk;
|
|
@@ -60522,7 +60529,7 @@ function computeProfitDetail(payload) {
|
|
|
60522
60529
|
reward_factor = strategy.reward_factor;
|
|
60523
60530
|
}
|
|
60524
60531
|
if (focus_position.avg_qty >= focus_position.quantity && strategy.max_reward_factor) {
|
|
60525
|
-
reward_factor = focus_position.quantity * strategy.max_reward_factor / focus_position.avg_qty;
|
|
60532
|
+
reward_factor = to_f2(focus_position.quantity * strategy.max_reward_factor / focus_position.avg_qty, "%.4f");
|
|
60526
60533
|
} else {
|
|
60527
60534
|
reward_factor = strategy.reward_factor;
|
|
60528
60535
|
}
|
|
@@ -60531,11 +60538,23 @@ function computeProfitDetail(payload) {
|
|
|
60531
60538
|
const pnl = to_f2(focus_position.entry * focus_position.quantity * profit_percent / 100, "%.2f");
|
|
60532
60539
|
const diff = pnl / focus_position.quantity;
|
|
60533
60540
|
const sell_price = to_f2(focus_position.kind === "long" ? focus_position.entry + diff : focus_position.entry - diff, price_places);
|
|
60534
|
-
|
|
60535
|
-
|
|
60536
|
-
|
|
60541
|
+
let loss = 0;
|
|
60542
|
+
let expected_loss = 0;
|
|
60543
|
+
let quantity = 0;
|
|
60544
|
+
let new_pnl = pnl;
|
|
60545
|
+
if (reduce_position) {
|
|
60546
|
+
loss = Math.abs(reduce_position.entry - sell_price) * reduce_position.quantity;
|
|
60547
|
+
const ratio = pnl / loss;
|
|
60548
|
+
quantity = to_f2(reduce_position.quantity * ratio, decimal_places);
|
|
60549
|
+
}
|
|
60550
|
+
if (reverse_position) {
|
|
60551
|
+
expected_loss = Math.abs(reverse_position.avg_price - sell_price) * reverse_position.avg_qty;
|
|
60552
|
+
new_pnl = to_f2(pnl - expected_loss, "%.2f");
|
|
60553
|
+
}
|
|
60537
60554
|
return {
|
|
60538
|
-
pnl,
|
|
60555
|
+
pnl: new_pnl,
|
|
60556
|
+
loss: to_f2(expected_loss, "%.2f"),
|
|
60557
|
+
original_pnl: pnl,
|
|
60539
60558
|
reward_factor,
|
|
60540
60559
|
profit_percent,
|
|
60541
60560
|
kind: focus_position.kind,
|
|
@@ -60545,6 +60564,38 @@ function computeProfitDetail(payload) {
|
|
|
60545
60564
|
decimal_places
|
|
60546
60565
|
};
|
|
60547
60566
|
}
|
|
60567
|
+
function generateGapTp(payload) {
|
|
60568
|
+
const {
|
|
60569
|
+
long,
|
|
60570
|
+
short,
|
|
60571
|
+
factor = 1,
|
|
60572
|
+
price_places = "%.1f",
|
|
60573
|
+
decimal_places = "%.3f"
|
|
60574
|
+
} = payload;
|
|
60575
|
+
const gap = Math.abs(long.entry - short.entry);
|
|
60576
|
+
const max_quantity = Math.max(long.quantity, short.quantity);
|
|
60577
|
+
const gapLoss = gap * max_quantity;
|
|
60578
|
+
const longPercent = gapLoss * factor / (short.entry * short.quantity);
|
|
60579
|
+
const shortPercent = gapLoss * factor / (long.entry * long.quantity);
|
|
60580
|
+
const longTp = to_f2((1 + longPercent) * long.entry, price_places);
|
|
60581
|
+
const shortTp = to_f2((1 + shortPercent) ** -1 * short.entry, price_places);
|
|
60582
|
+
const shortToReduce = to_f2(Math.abs(longTp - long.entry) * long.quantity, "%.1f");
|
|
60583
|
+
const longToReduce = to_f2(Math.abs(shortTp - short.entry) * short.quantity, "%.1f");
|
|
60584
|
+
const short_quantity_to_sell = determine_amount_to_sell2(short.entry, short.quantity, longTp, shortToReduce, "short", decimal_places);
|
|
60585
|
+
const long_quantity_to_sell = determine_amount_to_sell2(long.entry, long.quantity, shortTp, longToReduce, "long", decimal_places);
|
|
60586
|
+
return {
|
|
60587
|
+
long_tp: longTp,
|
|
60588
|
+
short_tp: shortTp,
|
|
60589
|
+
short_to_reduce: shortToReduce,
|
|
60590
|
+
long_to_reduce: longToReduce,
|
|
60591
|
+
to_sell: {
|
|
60592
|
+
short: short_quantity_to_sell,
|
|
60593
|
+
long: long_quantity_to_sell
|
|
60594
|
+
},
|
|
60595
|
+
gap: to_f2(gap, price_places),
|
|
60596
|
+
gap_loss: to_f2(gapLoss, "%.2f")
|
|
60597
|
+
};
|
|
60598
|
+
}
|
|
60548
60599
|
|
|
60549
60600
|
// src/helpers/strategy.ts
|
|
60550
60601
|
class Strategy {
|
|
@@ -60947,6 +60998,35 @@ class Strategy {
|
|
|
60947
60998
|
app_config.stop = this.to_f(app_config.stop);
|
|
60948
60999
|
return { ...app_config, avg, loss: -expected_loss, profit_percent };
|
|
60949
61000
|
}
|
|
61001
|
+
identifyGapConfig(payload) {
|
|
61002
|
+
const { factor = 1 } = payload;
|
|
61003
|
+
return generateGapTp({
|
|
61004
|
+
long: this.position.long,
|
|
61005
|
+
short: this.position.short,
|
|
61006
|
+
factor,
|
|
61007
|
+
decimal_places: this.config.global_config.decimal_places,
|
|
61008
|
+
price_places: this.config.global_config.price_places
|
|
61009
|
+
});
|
|
61010
|
+
}
|
|
61011
|
+
analyzeProfit(payload) {
|
|
61012
|
+
const { reward_factor = 1, max_reward_factor, risk, kind } = payload;
|
|
61013
|
+
const focus_position = this.position[kind];
|
|
61014
|
+
const result = computeProfitDetail({
|
|
61015
|
+
focus_position: {
|
|
61016
|
+
kind,
|
|
61017
|
+
entry: focus_position.entry,
|
|
61018
|
+
quantity: focus_position.quantity,
|
|
61019
|
+
avg_price: focus_position.avg_price,
|
|
61020
|
+
avg_qty: focus_position.avg_qty
|
|
61021
|
+
},
|
|
61022
|
+
strategy: {
|
|
61023
|
+
reward_factor,
|
|
61024
|
+
max_reward_factor,
|
|
61025
|
+
risk
|
|
61026
|
+
}
|
|
61027
|
+
});
|
|
61028
|
+
return result;
|
|
61029
|
+
}
|
|
60950
61030
|
}
|
|
60951
61031
|
|
|
60952
61032
|
// src/exchanges/binance.ts
|
|
@@ -64874,12 +64954,14 @@ class ExchangeAccount {
|
|
|
64874
64954
|
long: {
|
|
64875
64955
|
entry: long_position.entry,
|
|
64876
64956
|
quantity: long_position.quantity,
|
|
64877
|
-
avg_price: long_position.avg_price
|
|
64957
|
+
avg_price: long_position.avg_price,
|
|
64958
|
+
avg_qty: long_position.avg_qty
|
|
64878
64959
|
},
|
|
64879
64960
|
short: {
|
|
64880
64961
|
entry: short_position.entry,
|
|
64881
64962
|
quantity: short_position.quantity,
|
|
64882
|
-
avg_price: short_position.avg_price
|
|
64963
|
+
avg_price: short_position.avg_price,
|
|
64964
|
+
avg_qty: short_position.avg_qty
|
|
64883
64965
|
},
|
|
64884
64966
|
config: strategy_config
|
|
64885
64967
|
});
|
|
@@ -65734,6 +65816,31 @@ class ExchangeAccount {
|
|
|
65734
65816
|
}
|
|
65735
65817
|
};
|
|
65736
65818
|
}
|
|
65819
|
+
async generateGapTp(payload) {
|
|
65820
|
+
const { symbol, factor = 1 } = payload;
|
|
65821
|
+
const symbol_config = await this.recomputeSymbolConfig({
|
|
65822
|
+
symbol
|
|
65823
|
+
});
|
|
65824
|
+
const positions = await this.syncAccount({
|
|
65825
|
+
symbol,
|
|
65826
|
+
as_view: true
|
|
65827
|
+
});
|
|
65828
|
+
const long_position = positions.find((k) => k.kind === "long");
|
|
65829
|
+
const short_position = positions.find((k) => k.kind === "short");
|
|
65830
|
+
return generateGapTp({
|
|
65831
|
+
long: {
|
|
65832
|
+
entry: long_position.entry,
|
|
65833
|
+
quantity: long_position.quantity
|
|
65834
|
+
},
|
|
65835
|
+
short: {
|
|
65836
|
+
entry: short_position.entry,
|
|
65837
|
+
quantity: short_position.quantity
|
|
65838
|
+
},
|
|
65839
|
+
factor,
|
|
65840
|
+
price_places: symbol_config.price_places,
|
|
65841
|
+
decimal_places: symbol_config.decimal_places
|
|
65842
|
+
});
|
|
65843
|
+
}
|
|
65737
65844
|
async getSellPriceFromStrategy(payload) {
|
|
65738
65845
|
const { symbol, reduce_position } = payload;
|
|
65739
65846
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
@@ -65747,6 +65854,8 @@ class ExchangeAccount {
|
|
|
65747
65854
|
if (!focus_position) {
|
|
65748
65855
|
return;
|
|
65749
65856
|
}
|
|
65857
|
+
const reverse_kind = focus_position.kind === "long" ? "short" : "long";
|
|
65858
|
+
const reverse_position = positions.find((k) => k.kind === reverse_kind);
|
|
65750
65859
|
const strategy = focus_position?.expand?.account_strategy;
|
|
65751
65860
|
if (!strategy) {
|
|
65752
65861
|
return;
|
|
@@ -65771,6 +65880,12 @@ class ExchangeAccount {
|
|
|
65771
65880
|
avg_price: reduce_position.avg_price,
|
|
65772
65881
|
avg_qty: reduce_position.avg_qty
|
|
65773
65882
|
},
|
|
65883
|
+
reverse_position: {
|
|
65884
|
+
kind: reverse_position.kind,
|
|
65885
|
+
avg_price: reverse_position.avg_price,
|
|
65886
|
+
avg_qty: reverse_position.avg_qty,
|
|
65887
|
+
stop_loss: reverse_position.stop_loss
|
|
65888
|
+
},
|
|
65774
65889
|
price_places: symbol_config.price_places,
|
|
65775
65890
|
decimal_places: symbol_config.decimal_places
|
|
65776
65891
|
});
|