@gbozee/ultimate 0.0.2-97 → 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 +58 -2
- package/dist/frontend-index.js +76 -6
- package/dist/index.cjs +111 -8
- package/dist/index.d.ts +73 -2
- package/dist/index.js +111 -8
- package/dist/mcp-server.cjs +110 -8
- package/dist/mcp-server.js +110 -8
- package/package.json +1 -1
package/dist/frontend-index.d.ts
CHANGED
|
@@ -411,14 +411,14 @@ 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
|
|
421
|
+
reverse_position?: {
|
|
422
422
|
kind: "long" | "short";
|
|
423
423
|
avg_qty: number;
|
|
424
424
|
avg_price: number;
|
|
@@ -441,10 +441,35 @@ export declare function computeProfitDetail(payload: {
|
|
|
441
441
|
price_places: string;
|
|
442
442
|
decimal_places: string;
|
|
443
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
|
+
};
|
|
444
468
|
export type StrategyPosition = {
|
|
445
469
|
entry: number;
|
|
446
470
|
quantity: number;
|
|
447
471
|
avg_price?: number;
|
|
472
|
+
avg_qty?: number;
|
|
448
473
|
};
|
|
449
474
|
export type GapCloserResult = {
|
|
450
475
|
avg_entry: number;
|
|
@@ -626,6 +651,37 @@ export declare class Strategy {
|
|
|
626
651
|
rr?: number;
|
|
627
652
|
max_size?: number;
|
|
628
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
|
+
};
|
|
629
685
|
}
|
|
630
686
|
|
|
631
687
|
export {};
|
package/dist/frontend-index.js
CHANGED
|
@@ -1776,7 +1776,7 @@ function computeProfitDetail(payload) {
|
|
|
1776
1776
|
reward_factor = strategy.reward_factor;
|
|
1777
1777
|
}
|
|
1778
1778
|
if (focus_position.avg_qty >= focus_position.quantity && strategy.max_reward_factor) {
|
|
1779
|
-
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");
|
|
1780
1780
|
} else {
|
|
1781
1781
|
reward_factor = strategy.reward_factor;
|
|
1782
1782
|
}
|
|
@@ -1785,11 +1785,19 @@ function computeProfitDetail(payload) {
|
|
|
1785
1785
|
const pnl = to_f(focus_position.entry * focus_position.quantity * profit_percent / 100, "%.2f");
|
|
1786
1786
|
const diff = pnl / focus_position.quantity;
|
|
1787
1787
|
const sell_price = to_f(focus_position.kind === "long" ? focus_position.entry + diff : focus_position.entry - diff, price_places);
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
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
|
+
}
|
|
1793
1801
|
return {
|
|
1794
1802
|
pnl: new_pnl,
|
|
1795
1803
|
loss: to_f(expected_loss, "%.2f"),
|
|
@@ -1803,6 +1811,38 @@ function computeProfitDetail(payload) {
|
|
|
1803
1811
|
decimal_places
|
|
1804
1812
|
};
|
|
1805
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
|
+
}
|
|
1806
1846
|
// src/helpers/strategy.ts
|
|
1807
1847
|
class Strategy {
|
|
1808
1848
|
position;
|
|
@@ -2204,6 +2244,35 @@ class Strategy {
|
|
|
2204
2244
|
app_config.stop = this.to_f(app_config.stop);
|
|
2205
2245
|
return { ...app_config, avg, loss: -expected_loss, profit_percent };
|
|
2206
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
|
+
}
|
|
2207
2276
|
}
|
|
2208
2277
|
export {
|
|
2209
2278
|
to_f,
|
|
@@ -2222,6 +2291,7 @@ export {
|
|
|
2222
2291
|
getDecimalPlaces,
|
|
2223
2292
|
generate_config_params,
|
|
2224
2293
|
generateOptimumAppConfig,
|
|
2294
|
+
generateGapTp,
|
|
2225
2295
|
formatPrice,
|
|
2226
2296
|
fibonacci_analysis,
|
|
2227
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,
|
|
@@ -53856,7 +53863,7 @@ function computeProfitDetail(payload) {
|
|
|
53856
53863
|
reward_factor = strategy.reward_factor;
|
|
53857
53864
|
}
|
|
53858
53865
|
if (focus_position.avg_qty >= focus_position.quantity && strategy.max_reward_factor) {
|
|
53859
|
-
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");
|
|
53860
53867
|
} else {
|
|
53861
53868
|
reward_factor = strategy.reward_factor;
|
|
53862
53869
|
}
|
|
@@ -53865,11 +53872,19 @@ function computeProfitDetail(payload) {
|
|
|
53865
53872
|
const pnl = to_f2(focus_position.entry * focus_position.quantity * profit_percent / 100, "%.2f");
|
|
53866
53873
|
const diff = pnl / focus_position.quantity;
|
|
53867
53874
|
const sell_price = to_f2(focus_position.kind === "long" ? focus_position.entry + diff : focus_position.entry - diff, price_places);
|
|
53868
|
-
|
|
53869
|
-
|
|
53870
|
-
|
|
53871
|
-
|
|
53872
|
-
|
|
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
|
+
}
|
|
53873
53888
|
return {
|
|
53874
53889
|
pnl: new_pnl,
|
|
53875
53890
|
loss: to_f2(expected_loss, "%.2f"),
|
|
@@ -53883,6 +53898,38 @@ function computeProfitDetail(payload) {
|
|
|
53883
53898
|
decimal_places
|
|
53884
53899
|
};
|
|
53885
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
|
+
}
|
|
53886
53933
|
|
|
53887
53934
|
// src/helpers/strategy.ts
|
|
53888
53935
|
class Strategy {
|
|
@@ -54285,6 +54332,35 @@ class Strategy {
|
|
|
54285
54332
|
app_config.stop = this.to_f(app_config.stop);
|
|
54286
54333
|
return { ...app_config, avg, loss: -expected_loss, profit_percent };
|
|
54287
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
|
+
}
|
|
54288
54364
|
}
|
|
54289
54365
|
|
|
54290
54366
|
// src/exchanges/binance.ts
|
|
@@ -58212,12 +58288,14 @@ class ExchangeAccount {
|
|
|
58212
58288
|
long: {
|
|
58213
58289
|
entry: long_position.entry,
|
|
58214
58290
|
quantity: long_position.quantity,
|
|
58215
|
-
avg_price: long_position.avg_price
|
|
58291
|
+
avg_price: long_position.avg_price,
|
|
58292
|
+
avg_qty: long_position.avg_qty
|
|
58216
58293
|
},
|
|
58217
58294
|
short: {
|
|
58218
58295
|
entry: short_position.entry,
|
|
58219
58296
|
quantity: short_position.quantity,
|
|
58220
|
-
avg_price: short_position.avg_price
|
|
58297
|
+
avg_price: short_position.avg_price,
|
|
58298
|
+
avg_qty: short_position.avg_qty
|
|
58221
58299
|
},
|
|
58222
58300
|
config: strategy_config
|
|
58223
58301
|
});
|
|
@@ -59072,6 +59150,31 @@ class ExchangeAccount {
|
|
|
59072
59150
|
}
|
|
59073
59151
|
};
|
|
59074
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
|
+
}
|
|
59075
59178
|
async getSellPriceFromStrategy(payload) {
|
|
59076
59179
|
const { symbol, reduce_position } = payload;
|
|
59077
59180
|
const symbol_config = await this.recomputeSymbolConfig({
|
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,14 +1100,14 @@ 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
|
};
|
|
1078
|
-
reverse_position
|
|
1110
|
+
reverse_position?: {
|
|
1079
1111
|
kind: "long" | "short";
|
|
1080
1112
|
avg_qty: number;
|
|
1081
1113
|
avg_price: number;
|
|
@@ -1098,6 +1130,30 @@ export declare function computeProfitDetail(payload: {
|
|
|
1098
1130
|
price_places: string;
|
|
1099
1131
|
decimal_places: string;
|
|
1100
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
|
+
};
|
|
1101
1157
|
declare class ExchangePosition {
|
|
1102
1158
|
exchange: BaseExchange;
|
|
1103
1159
|
exchange_account: ExchangeAccount$1;
|
|
@@ -1856,6 +1912,21 @@ declare class ExchangeAccount$1 {
|
|
|
1856
1912
|
pnl: number;
|
|
1857
1913
|
};
|
|
1858
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
|
+
}>;
|
|
1859
1930
|
getSellPriceFromStrategy(payload: {
|
|
1860
1931
|
symbol: string;
|
|
1861
1932
|
reduce_position: PositionsView;
|
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,
|
|
@@ -53810,7 +53816,7 @@ function computeProfitDetail(payload) {
|
|
|
53810
53816
|
reward_factor = strategy.reward_factor;
|
|
53811
53817
|
}
|
|
53812
53818
|
if (focus_position.avg_qty >= focus_position.quantity && strategy.max_reward_factor) {
|
|
53813
|
-
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");
|
|
53814
53820
|
} else {
|
|
53815
53821
|
reward_factor = strategy.reward_factor;
|
|
53816
53822
|
}
|
|
@@ -53819,11 +53825,19 @@ function computeProfitDetail(payload) {
|
|
|
53819
53825
|
const pnl = to_f2(focus_position.entry * focus_position.quantity * profit_percent / 100, "%.2f");
|
|
53820
53826
|
const diff = pnl / focus_position.quantity;
|
|
53821
53827
|
const sell_price = to_f2(focus_position.kind === "long" ? focus_position.entry + diff : focus_position.entry - diff, price_places);
|
|
53822
|
-
|
|
53823
|
-
|
|
53824
|
-
|
|
53825
|
-
|
|
53826
|
-
|
|
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
|
+
}
|
|
53827
53841
|
return {
|
|
53828
53842
|
pnl: new_pnl,
|
|
53829
53843
|
loss: to_f2(expected_loss, "%.2f"),
|
|
@@ -53837,6 +53851,38 @@ function computeProfitDetail(payload) {
|
|
|
53837
53851
|
decimal_places
|
|
53838
53852
|
};
|
|
53839
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
|
+
}
|
|
53840
53886
|
|
|
53841
53887
|
// src/helpers/strategy.ts
|
|
53842
53888
|
class Strategy {
|
|
@@ -54239,6 +54285,35 @@ class Strategy {
|
|
|
54239
54285
|
app_config.stop = this.to_f(app_config.stop);
|
|
54240
54286
|
return { ...app_config, avg, loss: -expected_loss, profit_percent };
|
|
54241
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
|
+
}
|
|
54242
54317
|
}
|
|
54243
54318
|
|
|
54244
54319
|
// src/exchanges/binance.ts
|
|
@@ -58166,12 +58241,14 @@ class ExchangeAccount {
|
|
|
58166
58241
|
long: {
|
|
58167
58242
|
entry: long_position.entry,
|
|
58168
58243
|
quantity: long_position.quantity,
|
|
58169
|
-
avg_price: long_position.avg_price
|
|
58244
|
+
avg_price: long_position.avg_price,
|
|
58245
|
+
avg_qty: long_position.avg_qty
|
|
58170
58246
|
},
|
|
58171
58247
|
short: {
|
|
58172
58248
|
entry: short_position.entry,
|
|
58173
58249
|
quantity: short_position.quantity,
|
|
58174
|
-
avg_price: short_position.avg_price
|
|
58250
|
+
avg_price: short_position.avg_price,
|
|
58251
|
+
avg_qty: short_position.avg_qty
|
|
58175
58252
|
},
|
|
58176
58253
|
config: strategy_config
|
|
58177
58254
|
});
|
|
@@ -59026,6 +59103,31 @@ class ExchangeAccount {
|
|
|
59026
59103
|
}
|
|
59027
59104
|
};
|
|
59028
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
|
+
}
|
|
59029
59131
|
async getSellPriceFromStrategy(payload) {
|
|
59030
59132
|
const { symbol, reduce_position } = payload;
|
|
59031
59133
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
@@ -59531,6 +59633,7 @@ export {
|
|
|
59531
59633
|
getOptimumStopAndRisk,
|
|
59532
59634
|
generate_config_params,
|
|
59533
59635
|
generateOptimumAppConfig,
|
|
59636
|
+
generateGapTp,
|
|
59534
59637
|
exports_exchange_account as exchange_account,
|
|
59535
59638
|
determine_break_even_price,
|
|
59536
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,
|
|
@@ -60546,7 +60552,7 @@ function computeProfitDetail(payload) {
|
|
|
60546
60552
|
reward_factor = strategy.reward_factor;
|
|
60547
60553
|
}
|
|
60548
60554
|
if (focus_position.avg_qty >= focus_position.quantity && strategy.max_reward_factor) {
|
|
60549
|
-
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");
|
|
60550
60556
|
} else {
|
|
60551
60557
|
reward_factor = strategy.reward_factor;
|
|
60552
60558
|
}
|
|
@@ -60555,11 +60561,19 @@ function computeProfitDetail(payload) {
|
|
|
60555
60561
|
const pnl = to_f2(focus_position.entry * focus_position.quantity * profit_percent / 100, "%.2f");
|
|
60556
60562
|
const diff = pnl / focus_position.quantity;
|
|
60557
60563
|
const sell_price = to_f2(focus_position.kind === "long" ? focus_position.entry + diff : focus_position.entry - diff, price_places);
|
|
60558
|
-
|
|
60559
|
-
|
|
60560
|
-
|
|
60561
|
-
|
|
60562
|
-
|
|
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
|
+
}
|
|
60563
60577
|
return {
|
|
60564
60578
|
pnl: new_pnl,
|
|
60565
60579
|
loss: to_f2(expected_loss, "%.2f"),
|
|
@@ -60573,6 +60587,38 @@ function computeProfitDetail(payload) {
|
|
|
60573
60587
|
decimal_places
|
|
60574
60588
|
};
|
|
60575
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
|
+
}
|
|
60576
60622
|
|
|
60577
60623
|
// src/helpers/strategy.ts
|
|
60578
60624
|
class Strategy {
|
|
@@ -60975,6 +61021,35 @@ class Strategy {
|
|
|
60975
61021
|
app_config.stop = this.to_f(app_config.stop);
|
|
60976
61022
|
return { ...app_config, avg, loss: -expected_loss, profit_percent };
|
|
60977
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
|
+
}
|
|
60978
61053
|
}
|
|
60979
61054
|
|
|
60980
61055
|
// src/exchanges/binance.ts
|
|
@@ -64902,12 +64977,14 @@ class ExchangeAccount {
|
|
|
64902
64977
|
long: {
|
|
64903
64978
|
entry: long_position.entry,
|
|
64904
64979
|
quantity: long_position.quantity,
|
|
64905
|
-
avg_price: long_position.avg_price
|
|
64980
|
+
avg_price: long_position.avg_price,
|
|
64981
|
+
avg_qty: long_position.avg_qty
|
|
64906
64982
|
},
|
|
64907
64983
|
short: {
|
|
64908
64984
|
entry: short_position.entry,
|
|
64909
64985
|
quantity: short_position.quantity,
|
|
64910
|
-
avg_price: short_position.avg_price
|
|
64986
|
+
avg_price: short_position.avg_price,
|
|
64987
|
+
avg_qty: short_position.avg_qty
|
|
64911
64988
|
},
|
|
64912
64989
|
config: strategy_config
|
|
64913
64990
|
});
|
|
@@ -65762,6 +65839,31 @@ class ExchangeAccount {
|
|
|
65762
65839
|
}
|
|
65763
65840
|
};
|
|
65764
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
|
+
}
|
|
65765
65867
|
async getSellPriceFromStrategy(payload) {
|
|
65766
65868
|
const { symbol, reduce_position } = payload;
|
|
65767
65869
|
const symbol_config = await this.recomputeSymbolConfig({
|
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,
|
|
@@ -60523,7 +60529,7 @@ function computeProfitDetail(payload) {
|
|
|
60523
60529
|
reward_factor = strategy.reward_factor;
|
|
60524
60530
|
}
|
|
60525
60531
|
if (focus_position.avg_qty >= focus_position.quantity && strategy.max_reward_factor) {
|
|
60526
|
-
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");
|
|
60527
60533
|
} else {
|
|
60528
60534
|
reward_factor = strategy.reward_factor;
|
|
60529
60535
|
}
|
|
@@ -60532,11 +60538,19 @@ function computeProfitDetail(payload) {
|
|
|
60532
60538
|
const pnl = to_f2(focus_position.entry * focus_position.quantity * profit_percent / 100, "%.2f");
|
|
60533
60539
|
const diff = pnl / focus_position.quantity;
|
|
60534
60540
|
const sell_price = to_f2(focus_position.kind === "long" ? focus_position.entry + diff : focus_position.entry - diff, price_places);
|
|
60535
|
-
|
|
60536
|
-
|
|
60537
|
-
|
|
60538
|
-
|
|
60539
|
-
|
|
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
|
+
}
|
|
60540
60554
|
return {
|
|
60541
60555
|
pnl: new_pnl,
|
|
60542
60556
|
loss: to_f2(expected_loss, "%.2f"),
|
|
@@ -60550,6 +60564,38 @@ function computeProfitDetail(payload) {
|
|
|
60550
60564
|
decimal_places
|
|
60551
60565
|
};
|
|
60552
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
|
+
}
|
|
60553
60599
|
|
|
60554
60600
|
// src/helpers/strategy.ts
|
|
60555
60601
|
class Strategy {
|
|
@@ -60952,6 +60998,35 @@ class Strategy {
|
|
|
60952
60998
|
app_config.stop = this.to_f(app_config.stop);
|
|
60953
60999
|
return { ...app_config, avg, loss: -expected_loss, profit_percent };
|
|
60954
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
|
+
}
|
|
60955
61030
|
}
|
|
60956
61031
|
|
|
60957
61032
|
// src/exchanges/binance.ts
|
|
@@ -64879,12 +64954,14 @@ class ExchangeAccount {
|
|
|
64879
64954
|
long: {
|
|
64880
64955
|
entry: long_position.entry,
|
|
64881
64956
|
quantity: long_position.quantity,
|
|
64882
|
-
avg_price: long_position.avg_price
|
|
64957
|
+
avg_price: long_position.avg_price,
|
|
64958
|
+
avg_qty: long_position.avg_qty
|
|
64883
64959
|
},
|
|
64884
64960
|
short: {
|
|
64885
64961
|
entry: short_position.entry,
|
|
64886
64962
|
quantity: short_position.quantity,
|
|
64887
|
-
avg_price: short_position.avg_price
|
|
64963
|
+
avg_price: short_position.avg_price,
|
|
64964
|
+
avg_qty: short_position.avg_qty
|
|
64888
64965
|
},
|
|
64889
64966
|
config: strategy_config
|
|
64890
64967
|
});
|
|
@@ -65739,6 +65816,31 @@ class ExchangeAccount {
|
|
|
65739
65816
|
}
|
|
65740
65817
|
};
|
|
65741
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
|
+
}
|
|
65742
65844
|
async getSellPriceFromStrategy(payload) {
|
|
65743
65845
|
const { symbol, reduce_position } = payload;
|
|
65744
65846
|
const symbol_config = await this.recomputeSymbolConfig({
|