@gbozee/ultimate 0.0.2-next.4 → 0.0.2-next.41

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.
@@ -63321,12 +63321,18 @@ async function initPocketBaseClient(proxy_credentials) {
63321
63321
 
63322
63322
  class AppDatabase {
63323
63323
  pb;
63324
+ drizzlePb;
63324
63325
  email;
63325
63326
  salt;
63326
- constructor(pb, payload) {
63327
+ root_password;
63328
+ root_email;
63329
+ constructor(pb, payload, drizzlePb) {
63327
63330
  this.pb = pb;
63328
63331
  this.email = payload.email;
63329
63332
  this.salt = payload.salt;
63333
+ this.root_password = payload.db?.password;
63334
+ this.root_email = payload.db?.email;
63335
+ this.drizzlePb = drizzlePb;
63330
63336
  const customInspectSymbol = Symbol.for("nodejs.util.inspect.custom");
63331
63337
  this[customInspectSymbol] = function() {
63332
63338
  return `AppDatabase { pb: [PocketBase Instance - Sensitive Data Masked] }`;
@@ -63506,6 +63512,13 @@ class AppDatabase {
63506
63512
  return Array.from(new Set(positions.map((p) => p.symbol)));
63507
63513
  }
63508
63514
  async createOrUpdateLiveExchangeInstance(payload) {
63515
+ if (this.drizzlePb) {
63516
+ await this.drizzlePb?.createOrUpdateLiveExchangeInstance(payload);
63517
+ return this.getLiveExchangeInstance({
63518
+ account: payload.account,
63519
+ symbol: payload.symbol
63520
+ });
63521
+ }
63509
63522
  const result = await this.getLiveExchangeInstance(payload);
63510
63523
  if (result) {
63511
63524
  return await this.pb.collection("live_exchange_details").update(result.id, {
@@ -63521,6 +63534,9 @@ class AppDatabase {
63521
63534
  }
63522
63535
  }
63523
63536
  async getLiveExchangeInstance(payload) {
63537
+ if (this.drizzlePb) {
63538
+ return this.drizzlePb.getLiveExchangeInstance(payload);
63539
+ }
63524
63540
  const result = await this.pb.collection("live_exchange_details").getFullList({
63525
63541
  filter: `account.owner:lower="${payload.account.owner.toLowerCase()}" && account.exchange:lower="${payload.account.exchange.toLowerCase()}" && symbol:lower="${payload.symbol.toLowerCase()}"`,
63526
63542
  expand: "account"
@@ -63563,6 +63579,9 @@ class AppDatabase {
63563
63579
  });
63564
63580
  }
63565
63581
  async get_exchange_db_instance(account) {
63582
+ if (this.drizzlePb) {
63583
+ return this.drizzlePb.get_exchange_db_instance(account);
63584
+ }
63566
63585
  let filter = `owner="${account.owner}" && exchange="${account.exchange}"`;
63567
63586
  if (this.email) {
63568
63587
  const user = await this.getUserByEmail();
@@ -63586,11 +63605,18 @@ class AppDatabase {
63586
63605
  "resistance",
63587
63606
  "symbol_config",
63588
63607
  "anchor",
63589
- "watch_instance"
63608
+ "watch_instance",
63609
+ "short_compound_strategy.tracking_account.account"
63590
63610
  ];
63591
63611
  return fields.join(", ");
63592
63612
  }
63593
63613
  async fetchCentralPositions(payload) {
63614
+ if (this.drizzlePb) {
63615
+ return this.drizzlePb.fetchCentralPositions({
63616
+ ...payload,
63617
+ email: this.email
63618
+ });
63619
+ }
63594
63620
  const { asset: assetName, symbol, customFilter = "" } = payload;
63595
63621
  const pb = this.pb;
63596
63622
  let symbolFilter = assetName ? `symbol ~ "${assetName}"` : `symbol = "${symbol}"`;
@@ -63604,6 +63630,9 @@ class AppDatabase {
63604
63630
  return positionsWithAssetName;
63605
63631
  }
63606
63632
  async getPositions(options) {
63633
+ if (this.drizzlePb) {
63634
+ return this.drizzlePb.getPositions(options);
63635
+ }
63607
63636
  const { symbol, as_view = false, account, custom_filter } = options;
63608
63637
  let default_params = as_view ? {
63609
63638
  table: "positions_view",
@@ -63642,6 +63671,9 @@ class AppDatabase {
63642
63671
  account,
63643
63672
  symbol
63644
63673
  });
63674
+ if (this.drizzlePb) {
63675
+ return db_positions;
63676
+ }
63645
63677
  const exchange_db_instance = await this.get_exchange_db_instance(account);
63646
63678
  const long_exists = db_positions.find((x) => x.kind === "long");
63647
63679
  const short_exists = db_positions.find((x) => x.kind === "short");
@@ -63664,9 +63696,15 @@ class AppDatabase {
63664
63696
  return db_positions;
63665
63697
  }
63666
63698
  async update_db_position(position, payload) {
63699
+ if (this.drizzlePb) {
63700
+ return this.drizzlePb.update_db_position(position, payload);
63701
+ }
63667
63702
  return await this.pb.collection("positions").update(position.id, payload);
63668
63703
  }
63669
63704
  async getSymbolConfigFromDB(symbol) {
63705
+ if (this.drizzlePb) {
63706
+ return this.drizzlePb.getSymbolConfigFromDB(symbol);
63707
+ }
63670
63708
  const item = await this.pb.collection("symbol_configs").getFullList({
63671
63709
  filter: `symbol:lower="${symbol.toLowerCase()}"`
63672
63710
  });
@@ -63676,6 +63714,9 @@ class AppDatabase {
63676
63714
  return null;
63677
63715
  }
63678
63716
  async getOrders(account, options) {
63717
+ if (this.drizzlePb) {
63718
+ return this.drizzlePb.getOrders(account, options);
63719
+ }
63679
63720
  const { symbol, kind } = options;
63680
63721
  const db_orders = await this.pb.collection("orders").getFullList({
63681
63722
  filter: `symbol:lower="${symbol.toLowerCase()}" && account.owner:lower="${account.owner.toLowerCase()}" && account.exchange:lower="${account.exchange.toLowerCase()}" && kind="${kind}"`,
@@ -63742,20 +63783,99 @@ class AppDatabase {
63742
63783
  });
63743
63784
  }
63744
63785
  async deleteAndBulCreateAllOrders(payload) {
63786
+ if (this.drizzlePb) {
63787
+ return this.drizzlePb.getOrders(payload.account, {
63788
+ symbol: payload.symbol,
63789
+ kind: "long"
63790
+ });
63791
+ }
63745
63792
  const { account, symbol, all_orders } = payload;
63746
- console.log(`Fetching existing DB orders for ${account.owner}/${account.exchange} - ${symbol} to delete...`);
63747
- let pocketbase_ids_to_delete = [];
63793
+ console.log(`Fetching existing DB orders for ${account.owner}/${account.exchange} - ${symbol} for deduplication...`);
63794
+ let existing_orders = [];
63748
63795
  try {
63749
- const db_orders_to_delete = await this.pb.collection("orders").getFullList({
63796
+ existing_orders = await this.pb.collection("orders").getFullList({
63750
63797
  filter: `symbol:lower="${symbol.toLowerCase()}" && account.owner:lower="${account.owner.toLowerCase()}" && account.exchange:lower="${account.exchange.toLowerCase()}"`
63751
63798
  });
63752
- pocketbase_ids_to_delete = db_orders_to_delete.map((o) => o.id);
63753
- console.log(`Found ${pocketbase_ids_to_delete.length} existing DB orders to delete.`);
63799
+ console.log(`Found ${existing_orders.length} existing DB orders.`);
63754
63800
  } catch (error) {
63755
- console.error("Error fetching DB orders for deletion:", error);
63801
+ console.error("Error fetching existing DB orders:", error);
63802
+ existing_orders = [];
63803
+ }
63804
+ const existing_signatures = new Set;
63805
+ const existing_ids_to_delete = new Set;
63806
+ for (const existing_order of existing_orders) {
63807
+ const signature = [
63808
+ existing_order.symbol?.toLowerCase() || "",
63809
+ existing_order.kind || "",
63810
+ existing_order.price?.toString() || "",
63811
+ existing_order.quantity?.toString() || "",
63812
+ existing_order.side?.toLowerCase() || "",
63813
+ existing_order.stop?.toString() || "0",
63814
+ existing_order.order_id?.toString() || "",
63815
+ existing_order.client_order_id?.toString() || "",
63816
+ account.id
63817
+ ].join("|");
63818
+ existing_signatures.add(signature);
63819
+ existing_ids_to_delete.add(existing_order.id);
63820
+ }
63821
+ const unique_orders = [];
63822
+ const new_signatures = new Set;
63823
+ for (const order of all_orders) {
63824
+ const signature = [
63825
+ symbol.toLowerCase(),
63826
+ order.kind || "",
63827
+ order.price?.toString() || "",
63828
+ order.quantity?.toString() || "",
63829
+ order.side?.toLowerCase() || "",
63830
+ (order.stop || order.triggerPrice || 0).toString(),
63831
+ order.order_id?.toString() || "",
63832
+ (order.clientOrderId || "").toString(),
63833
+ account.id
63834
+ ].join("|");
63835
+ if (!existing_signatures.has(signature) && !new_signatures.has(signature)) {
63836
+ unique_orders.push(order);
63837
+ new_signatures.add(signature);
63838
+ } else {
63839
+ console.log(`Skipping duplicate order: ${order.order_id} for ${symbol}`);
63840
+ }
63841
+ }
63842
+ console.log(`After deduplication: ${unique_orders.length} unique orders out of ${all_orders.length} total orders`);
63843
+ const orders_to_keep = new Set;
63844
+ for (const order of all_orders) {
63845
+ const signature = [
63846
+ symbol.toLowerCase(),
63847
+ order.kind || "",
63848
+ order.price?.toString() || "",
63849
+ order.quantity?.toString() || "",
63850
+ order.side?.toLowerCase() || "",
63851
+ (order.stop || order.triggerPrice || 0).toString(),
63852
+ order.order_id?.toString() || "",
63853
+ (order.clientOrderId || "").toString(),
63854
+ account.id
63855
+ ].join("|");
63856
+ if (existing_signatures.has(signature)) {
63857
+ orders_to_keep.add(signature);
63858
+ }
63859
+ }
63860
+ const pocketbase_ids_to_delete = [];
63861
+ for (const existing_order of existing_orders) {
63862
+ const signature = [
63863
+ existing_order.symbol?.toLowerCase() || "",
63864
+ existing_order.kind || "",
63865
+ existing_order.price?.toString() || "",
63866
+ existing_order.quantity?.toString() || "",
63867
+ existing_order.side?.toLowerCase() || "",
63868
+ existing_order.stop?.toString() || "0",
63869
+ existing_order.order_id?.toString() || "",
63870
+ existing_order.client_order_id?.toString() || "",
63871
+ account.id
63872
+ ].join("|");
63873
+ if (!orders_to_keep.has(signature)) {
63874
+ pocketbase_ids_to_delete.push(existing_order.id);
63875
+ }
63756
63876
  }
63757
63877
  if (pocketbase_ids_to_delete.length > 0) {
63758
- console.log(`Deleting ${pocketbase_ids_to_delete.length} existing orders from PocketBase...`);
63878
+ console.log(`Deleting ${pocketbase_ids_to_delete.length} obsolete orders from PocketBase...`);
63759
63879
  for (let i2 = 0;i2 < pocketbase_ids_to_delete.length; i2 += 5) {
63760
63880
  const batch_to_delete = pocketbase_ids_to_delete.slice(i2, i2 + 5);
63761
63881
  try {
@@ -63764,13 +63884,12 @@ class AppDatabase {
63764
63884
  console.error(`Error deleting batch starting at index ${i2}:`, error, "Batch IDs:", batch_to_delete);
63765
63885
  }
63766
63886
  }
63767
- console.log("Finished deleting existing DB orders.");
63887
+ console.log("Finished deleting obsolete DB orders.");
63768
63888
  }
63769
- console.log(`Found ${all_orders.length} current orders on the exchange.`);
63770
- if (all_orders.length > 0) {
63771
- console.log(`Recreating ${all_orders.length} orders in PocketBase...`);
63772
- for (let i2 = 0;i2 < all_orders.length; i2 += 100) {
63773
- const orderChunk = all_orders.slice(i2, i2 + 100);
63889
+ if (unique_orders.length > 0) {
63890
+ console.log(`Creating ${unique_orders.length} new orders in PocketBase...`);
63891
+ for (let i2 = 0;i2 < unique_orders.length; i2 += 100) {
63892
+ const orderChunk = unique_orders.slice(i2, i2 + 100);
63774
63893
  const batch2 = this.pb.createBatch();
63775
63894
  for (const order of orderChunk) {
63776
63895
  const order_data = {
@@ -63792,8 +63911,9 @@ class AppDatabase {
63792
63911
  console.error(`Error creating batch starting at index ${i2}:`, error);
63793
63912
  }
63794
63913
  }
63795
- console.log("Finished recreating orders in PocketBase.");
63914
+ console.log("Finished creating new orders in PocketBase.");
63796
63915
  }
63916
+ console.log(`Order synchronization complete. Created ${unique_orders.length} new orders, deleted ${pocketbase_ids_to_delete.length} obsolete orders.`);
63797
63917
  }
63798
63918
  async cancelLimitOrders(payload) {
63799
63919
  const { symbol, kind, account, cancelExchangeOrders, raw } = payload;
@@ -63808,7 +63928,7 @@ class AppDatabase {
63808
63928
  const stop_limit_orders = await this.pb.collection("orders").getFullList({
63809
63929
  filter: `symbol:lower="${symbol.toLowerCase()}" && account.owner:lower="${account.owner.toLowerCase()}" && account.exchange:lower="${account.exchange.toLowerCase()}" && kind="${kind}" && side:lower="${side}" && stop > 0`
63810
63930
  });
63811
- const exchange_order_ids = orders.concat(existing_stop_orders).concat(stop_limit_orders).map((o) => account.exchange === "bybit" ? o.order_id : account.exchange === "binance" && o.client_order_id ? o.client_order_id : parseInt(o.order_id, 10));
63931
+ const exchange_order_ids = orders.concat(existing_stop_orders).concat(stop_limit_orders).map((o) => account.exchange === "bybit" ? o.order_id : ["binance", "paper"].includes(account.exchange) && o.client_order_id ? o.client_order_id : parseInt(o.order_id, 10));
63812
63932
  if (raw) {
63813
63933
  return exchange_order_ids;
63814
63934
  }
@@ -63882,6 +64002,9 @@ class AppDatabase {
63882
64002
  return result;
63883
64003
  }
63884
64004
  async updateScheduledTrade(id, payload) {
64005
+ if (this.drizzlePb) {
64006
+ return this.drizzlePb.updateScheduledTrade(id, payload);
64007
+ }
63885
64008
  return await this.pb.collection("scheduled_trades").update(id, payload);
63886
64009
  }
63887
64010
  async getPositionsToAutoFollow() {
@@ -64135,6 +64258,9 @@ class AppDatabase {
64135
64258
  return { updated_bullish, moved_to_winding };
64136
64259
  }
64137
64260
  async updateSymbolConfigs(payload) {
64261
+ if (this.drizzlePb) {
64262
+ return this.drizzlePb.updateSymbolConfigs(payload);
64263
+ }
64138
64264
  if (!payload || !payload.configs) {
64139
64265
  console.log("No payload provided. Fetching all symbol configs...");
64140
64266
  try {
@@ -64452,8 +64578,7 @@ function getEntries(params) {
64452
64578
  margin_range,
64453
64579
  risk_reward,
64454
64580
  kind,
64455
- price_places,
64456
- percent_change: distribution_params?.curveFactor
64581
+ price_places
64457
64582
  });
64458
64583
  break;
64459
64584
  case "normal":
@@ -64483,12 +64608,37 @@ function getEntries(params) {
64483
64608
  curveFactor: distribution_params?.curveFactor
64484
64609
  });
64485
64610
  break;
64611
+ case "lognormal":
64612
+ entries = generateLognormal({
64613
+ margin_range,
64614
+ risk_reward,
64615
+ kind,
64616
+ price_places,
64617
+ stdDevFactor: distribution_params?.stdDevFactor
64618
+ });
64619
+ break;
64486
64620
  default:
64487
64621
  throw new Error(`Unknown distribution type: ${distribution}`);
64488
64622
  }
64489
64623
  return entries.sort((a, b) => a - b);
64490
64624
  }
64491
64625
  var distributions_default = getEntries;
64626
+ function generateLognormal(payload) {
64627
+ const { margin_range, risk_reward, kind, price_places = "%.1f", stdDevFactor = 6 } = payload;
64628
+ const logMin = Math.log(margin_range[0]);
64629
+ const logMax = Math.log(margin_range[1]);
64630
+ const mean = (logMin + logMax) / 2;
64631
+ const stdDev = Math.abs(logMax - logMin) / stdDevFactor;
64632
+ const entries = Array.from({ length: risk_reward + 1 }, (_, i2) => {
64633
+ const p = (i2 + 0.5) / (risk_reward + 1);
64634
+ const z2 = approximateInverseNormal(p);
64635
+ let logPrice = mean + stdDev * z2;
64636
+ logPrice = Math.max(logMin, Math.min(logMax, logPrice));
64637
+ const price = Math.exp(logPrice);
64638
+ return to_f(kind === "long" ? Math.min(price, margin_range[1]) : Math.max(price, margin_range[0]), price_places);
64639
+ });
64640
+ return entries.sort((a, b) => a - b);
64641
+ }
64492
64642
 
64493
64643
  // src/helpers/optimizations.ts
64494
64644
  function calculateTheoreticalKelly({
@@ -64862,7 +65012,7 @@ class Signal {
64862
65012
  const simple_support = Math.min(current_price, stop_loss);
64863
65013
  const simple_resistance = Math.max(current_price, stop_loss);
64864
65014
  const risk_per_trade = risk / this.risk_reward;
64865
- const use_progressive = distribution_params.use_progressive || this.use_progressive_risk;
65015
+ const use_progressive = distribution_params?.use_progressive || this.use_progressive_risk;
64866
65016
  const risk_distribution = use_progressive ? {
64867
65017
  enabled: true,
64868
65018
  total_risk_budget: risk,
@@ -65331,7 +65481,6 @@ class Signal {
65331
65481
  const defaultStopLoss = i2 === 0 ? stop_loss : _base;
65332
65482
  const new_stop = kind === "long" ? this.support : stop_loss;
65333
65483
  let risk_to_use = this.getZoneRisk(i2, limit_orders.length, this);
65334
- console.log("index: ", i2, " risk: ", risk_to_use);
65335
65484
  if (this.use_kelly) {
65336
65485
  const func = this.kelly_func === "theoretical" ? calculateTheoreticalKelly : this.kelly_func === "position_based" ? calculatePositionBasedKelly : calculateTheoreticalKellyFixed;
65337
65486
  const theoretical_kelly = func({
@@ -65575,6 +65724,9 @@ var pnl_default = value;
65575
65724
 
65576
65725
  // src/helpers/trade_utils.ts
65577
65726
  function to_f(value2, places = "%.1f") {
65727
+ if (!value2) {
65728
+ return null;
65729
+ }
65578
65730
  let v = typeof value2 === "string" ? parseFloat(value2) : value2;
65579
65731
  const formattedValue = places.replace("%.", "").replace("f", "");
65580
65732
  return parseFloat(v.toFixed(parseInt(formattedValue)));
@@ -65805,7 +65957,7 @@ function buildConfig(app_config, {
65805
65957
  min_avg_size = 0,
65806
65958
  distribution,
65807
65959
  distribution_params,
65808
- use_progressive_risk
65960
+ use_progressive_risk = false
65809
65961
  }) {
65810
65962
  let fee = app_config.fee / 100;
65811
65963
  let working_risk = risk || app_config.risk_per_trade;
@@ -65847,7 +65999,7 @@ function buildConfig(app_config, {
65847
65999
  if (!stop) {
65848
66000
  return [];
65849
66001
  }
65850
- const condition = (kind === "long" ? entry > app_config.support : entry >= app_config.support) && stop >= app_config.support * 0.999;
66002
+ const condition = true;
65851
66003
  if (kind === "short") {}
65852
66004
  const result = entry === stop ? [] : condition ? instance.build_entry({
65853
66005
  current_price: entry,
@@ -66317,7 +66469,7 @@ function determineOptimumReward(payload) {
66317
66469
  const criterion = app_config.strategy || "quantity";
66318
66470
  const risk_rewards = createArray(low_range, high_range, 1);
66319
66471
  let func = risk_rewards.map((trade_no) => {
66320
- let trades = sortedBuildConfig(app_config, {
66472
+ const pp = {
66321
66473
  take_profit: app_config.take_profit,
66322
66474
  entry: app_config.entry,
66323
66475
  stop: app_config.stop,
@@ -66329,7 +66481,8 @@ function determineOptimumReward(payload) {
66329
66481
  decimal_places: app_config.decimal_places,
66330
66482
  distribution,
66331
66483
  distribution_params: payload.distribution_params
66332
- });
66484
+ };
66485
+ let trades = sortedBuildConfig(app_config, pp);
66333
66486
  let total = 0;
66334
66487
  let max = -Infinity;
66335
66488
  let min = Infinity;
@@ -66420,7 +66573,6 @@ function findIndexByCondition(lst, kind, condition, defaultKey = "neg_pnl") {
66420
66573
  return b.net_diff - a.net_diff;
66421
66574
  }
66422
66575
  });
66423
- console.log("found", sortedFound);
66424
66576
  if (defaultKey === "quantity") {
66425
66577
  return sortedFound[0].index;
66426
66578
  }
@@ -67527,10 +67679,14 @@ function helperFuncToBuildTrades({
67527
67679
  symbol_config,
67528
67680
  app_config_kind,
67529
67681
  appConfig,
67530
- force_exact_risk = true
67682
+ force_exact_risk = true,
67683
+ use_default = false
67531
67684
  }) {
67532
67685
  const risk = custom_b_config.risk * (custom_b_config.risk_factor || 1);
67533
- let result = getRiskReward({
67686
+ let result = use_default ? {
67687
+ risk,
67688
+ risk_reward: custom_b_config.risk_reward
67689
+ } : getRiskReward({
67534
67690
  entry: custom_b_config.entry,
67535
67691
  stop: custom_b_config.stop,
67536
67692
  risk,
@@ -67579,7 +67735,8 @@ function buildWithOptimumReward({
67579
67735
  config: config2,
67580
67736
  settings,
67581
67737
  global_config,
67582
- force_exact
67738
+ force_exact,
67739
+ use_default = false
67583
67740
  }) {
67584
67741
  const kind = config2.entry > config2.stop ? "long" : "short";
67585
67742
  let stop = settings.stop;
@@ -67593,7 +67750,8 @@ function buildWithOptimumReward({
67593
67750
  stop,
67594
67751
  risk,
67595
67752
  distribution,
67596
- distribution_params
67753
+ distribution_params,
67754
+ risk_reward: settings.risk_reward || config2?.risk_reward
67597
67755
  };
67598
67756
  const appConfig = constructAppConfig2({
67599
67757
  config: config2,
@@ -67604,7 +67762,8 @@ function buildWithOptimumReward({
67604
67762
  app_config_kind: kind,
67605
67763
  appConfig,
67606
67764
  symbol_config: global_config,
67607
- force_exact_risk: force_exact
67765
+ force_exact_risk: force_exact,
67766
+ use_default
67608
67767
  });
67609
67768
  const adjusted_size = summary.quantity;
67610
67769
  const symbol_config = global_config;
@@ -67954,6 +68113,7 @@ class BaseExchange {
67954
68113
  });
67955
68114
  }
67956
68115
  }
68116
+ async placeSpotLimitOrders(_payload) {}
67957
68117
  async bulkPlaceLimitOrders(payload) {
67958
68118
  const {
67959
68119
  orders,
@@ -68540,8 +68700,25 @@ var ORDERS_PER_MINUTE = 2000;
68540
68700
  var ORDERS_PER_SECOND = Math.floor(ORDERS_PER_MINUTE / 60);
68541
68701
  var BATCH_SIZE = 5;
68542
68702
  var CANCEL_BATCH_SIZE = 10;
68543
- async function createLimitPurchaseOrdersParallel(client, symbol, priceFormat, quantityFormat, orders, currentPrice, workingType = "last", realClose = false) {
68703
+ async function createLimitPurchaseOrdersParallel(client, symbol, priceFormat, quantityFormat, _orders, currentPrice, workingType = "last", realClose = false) {
68544
68704
  const workingTypeValue = workingType === "mark" ? "MARK_PRICE" : "CONTRACT_PRICE";
68705
+ const orders = _orders.map((u) => {
68706
+ if (currentPrice && !u.stop) {
68707
+ if (u.kind === "long" && u.price > currentPrice && u.side === "buy") {
68708
+ return {
68709
+ ...u,
68710
+ force_market: true
68711
+ };
68712
+ }
68713
+ if (u.kind === "short" && u.price < currentPrice && u.side === "sell") {
68714
+ return {
68715
+ ...u,
68716
+ force_market: true
68717
+ };
68718
+ }
68719
+ }
68720
+ return u;
68721
+ });
68545
68722
  const splitOrders = (inputOrders) => {
68546
68723
  const result = [];
68547
68724
  for (const o of inputOrders) {
@@ -68624,6 +68801,137 @@ async function createLimitPurchaseOrdersParallel(client, symbol, priceFormat, qu
68624
68801
  })));
68625
68802
  return results;
68626
68803
  }
68804
+ async function createSpotLimitOrdersParallel(client, symbol, priceFormat, quantityFormat, orders, currentPrice) {
68805
+ const createSpotOrder = (x) => {
68806
+ const v = {
68807
+ symbol: symbol.toUpperCase(),
68808
+ price: x.price ? to_f(x.price, priceFormat) : undefined,
68809
+ quantity: x.quantity ? to_f(x.quantity, quantityFormat) : 0,
68810
+ side: x.side?.toUpperCase() || "",
68811
+ type: "LIMIT",
68812
+ timeInForce: "GTC"
68813
+ };
68814
+ if (x.stop && x.stop > 0) {
68815
+ v.type = "STOP_LOSS_LIMIT";
68816
+ v.stopPrice = to_f(x.stop, priceFormat);
68817
+ v.timeInForce = "GTC";
68818
+ }
68819
+ if (x.force_market || x.is_market) {
68820
+ delete v.price;
68821
+ delete v.timeInForce;
68822
+ v.type = "MARKET";
68823
+ }
68824
+ if (x.timeInForce) {
68825
+ v.timeInForce = x.timeInForce;
68826
+ }
68827
+ return v;
68828
+ };
68829
+ const newOrders = orders.map(createSpotOrder);
68830
+ const batches = [];
68831
+ for (let i2 = 0;i2 < newOrders.length; i2 += BATCH_SIZE) {
68832
+ batches.push(newOrders.slice(i2, i2 + BATCH_SIZE));
68833
+ }
68834
+ const limit = import_p_limit.default(ORDERS_PER_SECOND);
68835
+ const results = await Promise.all(batches.map((batch2) => limit(async () => {
68836
+ try {
68837
+ const batchResults = await Promise.all(batch2.map(async (orderPayload) => {
68838
+ const cleanPayload = Object.fromEntries(Object.entries(orderPayload).filter(([, value2]) => value2 !== undefined));
68839
+ return await client.submitNewOrder(cleanPayload);
68840
+ }));
68841
+ console.log("Spot batch result:", batchResults);
68842
+ return batchResults;
68843
+ } catch (error) {
68844
+ console.error("Error processing spot batch:", error);
68845
+ throw error;
68846
+ }
68847
+ })));
68848
+ return results.flat();
68849
+ }
68850
+ async function placeSpotLimitOrders(client, payload) {
68851
+ const { price_places = "%.8f", decimal_places = "%.8f", symbol } = payload;
68852
+ if (payload.cancel) {
68853
+ await cancelSpotOrders(client, symbol);
68854
+ }
68855
+ if (payload.orders && payload.orders.length > 0) {
68856
+ const orders = payload.orders.map((x) => ({
68857
+ side: x.side,
68858
+ price: x.price,
68859
+ quantity: x.quantity
68860
+ }));
68861
+ return await createSpotLimitOrdersParallel(client, symbol, price_places, decimal_places, orders);
68862
+ }
68863
+ }
68864
+ async function cancelSpotOrders(client, symbol, orders) {
68865
+ if (orders && orders.length > 0) {
68866
+ for (const order of orders) {
68867
+ try {
68868
+ if (order.orderId) {
68869
+ await client.cancelOrder({
68870
+ symbol,
68871
+ orderId: order.orderId
68872
+ });
68873
+ } else if (order.origClientOrderId) {
68874
+ await client.cancelOrder({
68875
+ symbol,
68876
+ origClientOrderId: order.origClientOrderId
68877
+ });
68878
+ }
68879
+ } catch (error) {
68880
+ console.error("Error cancelling spot order:", error);
68881
+ }
68882
+ }
68883
+ } else {
68884
+ try {
68885
+ const openOrders = await client.getOpenOrders({ symbol });
68886
+ for (const order of openOrders) {
68887
+ await client.cancelOrder({
68888
+ symbol,
68889
+ orderId: order.orderId
68890
+ });
68891
+ }
68892
+ } catch (error) {
68893
+ console.error("Error cancelling all spot orders:", error);
68894
+ }
68895
+ }
68896
+ }
68897
+ async function getSpotOpenOrders(client, symbol) {
68898
+ try {
68899
+ const response = await client.getOpenOrders({
68900
+ symbol
68901
+ });
68902
+ return (response || []).map((order) => ({
68903
+ ...order,
68904
+ id: order.orderId,
68905
+ order_id: order.orderId,
68906
+ price: parseFloat(order.price),
68907
+ quantity: parseFloat(order.origQty),
68908
+ side: order.side.toLowerCase(),
68909
+ type: order.type.toLowerCase(),
68910
+ status: order.status.toLowerCase()
68911
+ }));
68912
+ } catch (error) {
68913
+ console.error("Error fetching spot open orders:", error);
68914
+ return [];
68915
+ }
68916
+ }
68917
+ async function getSpotBalances(client, assets) {
68918
+ try {
68919
+ const response = await client.getAccountInformation();
68920
+ let balances = response.balances.map((balance) => ({
68921
+ asset: balance.asset,
68922
+ free: parseFloat(balance.free),
68923
+ locked: parseFloat(balance.locked),
68924
+ total: parseFloat(balance.free) + parseFloat(balance.locked)
68925
+ }));
68926
+ if (assets && assets.length > 0) {
68927
+ balances = balances.filter((balance) => assets.some((asset) => asset.toUpperCase() === balance.asset.toUpperCase()));
68928
+ }
68929
+ return balances.filter((balance) => balance.total > 0);
68930
+ } catch (error) {
68931
+ console.error("Error fetching spot balances:", error);
68932
+ return [];
68933
+ }
68934
+ }
68627
68935
  async function getPositionInfo(client, symbol) {
68628
68936
  const response = await client.getPositionsV3({
68629
68937
  symbol
@@ -69140,7 +69448,8 @@ class BinanceExchange extends BaseExchange {
69140
69448
  return await cancelAllOrders(this.client, symbol, payload);
69141
69449
  }
69142
69450
  async _createLimitPurchaseOrders(payload) {
69143
- return await createLimitPurchaseOrdersParallel(this.client, payload.symbol, payload.price_places, payload.decimal_places, payload.orders);
69451
+ const current_price = await this.getCurrentPrice(payload.symbol);
69452
+ return await createLimitPurchaseOrdersParallel(this.client, payload.symbol, payload.price_places, payload.decimal_places, payload.orders, current_price);
69144
69453
  }
69145
69454
  async analyzeCharts(payload) {
69146
69455
  return await analyzeCharts({
@@ -69475,6 +69784,51 @@ class BinanceExchange extends BaseExchange {
69475
69784
  };
69476
69785
  }
69477
69786
  }
69787
+ async createSpotLimitOrders(payload) {
69788
+ if (!this.main_client) {
69789
+ throw new Error("Main client not available for spot trading");
69790
+ }
69791
+ return await createSpotLimitOrdersParallel(this.main_client, payload.symbol, payload.price_places || "%.8f", payload.decimal_places || "%.8f", payload.orders);
69792
+ }
69793
+ async placeSpotLimitOrders(payload) {
69794
+ if (!this.main_client) {
69795
+ throw new Error("Main client not available for spot trading");
69796
+ }
69797
+ return await placeSpotLimitOrders(this.main_client, payload);
69798
+ }
69799
+ async cancelSpotOrders(symbol, orders) {
69800
+ if (!this.main_client) {
69801
+ throw new Error("Main client not available for spot trading");
69802
+ }
69803
+ return await cancelSpotOrders(this.main_client, symbol, orders);
69804
+ }
69805
+ async getSpotOpenOrders(symbol) {
69806
+ if (!this.main_client) {
69807
+ throw new Error("Main client not available for spot trading");
69808
+ }
69809
+ return await getSpotOpenOrders(this.main_client, symbol);
69810
+ }
69811
+ async getSpotBalances(assets) {
69812
+ if (!this.main_client) {
69813
+ throw new Error("Main client not available for spot trading");
69814
+ }
69815
+ return await getSpotBalances(this.main_client, assets);
69816
+ }
69817
+ async getSpotCurrentPrice(symbol) {
69818
+ if (!this.main_client) {
69819
+ throw new Error("Main client not available for spot trading");
69820
+ }
69821
+ try {
69822
+ const response = await this.main_client.getSymbolPriceTicker({
69823
+ symbol: symbol.toUpperCase()
69824
+ });
69825
+ const price = Array.isArray(response) ? response[0]?.price : response.price;
69826
+ return parseFloat(price);
69827
+ } catch (error) {
69828
+ console.error("Error fetching spot current price:", error);
69829
+ throw error;
69830
+ }
69831
+ }
69478
69832
  }
69479
69833
  function getPricePlaces(target) {
69480
69834
  const numStr = target.toString();
@@ -70813,7 +71167,7 @@ class ExchangePosition {
70813
71167
  return compound_instance;
70814
71168
  }
70815
71169
  async getProxyForAccount() {
70816
- if (this.instance.expand.proxy) {
71170
+ if (this.instance?.expand?.proxy) {
70817
71171
  const result = this.instance.expand.proxy;
70818
71172
  const { type, ip_address } = result;
70819
71173
  console.log("exchange", {
@@ -70882,11 +71236,21 @@ class ExchangePosition {
70882
71236
  if (this.instance.expand?.b_config) {
70883
71237
  return this.instance.expand.b_config;
70884
71238
  }
70885
- return await this.app_db.getPositionConfig({
71239
+ const result = await this.app_db.getPositionConfig({
70886
71240
  symbol: this.symbol,
70887
71241
  kind: this.kind,
70888
71242
  account: this.account
70889
71243
  });
71244
+ if (!result) {
71245
+ const entry = this.kind === "long" ? this.symbol_config.resistance : this.symbol_config.support;
71246
+ const stop = this.kind === "long" ? this.symbol_config.support : this.symbol_config.resistance;
71247
+ const params = {
71248
+ entry,
71249
+ stop
71250
+ };
71251
+ return this.getConfig({ params });
71252
+ }
71253
+ return result;
70890
71254
  }
70891
71255
  async updateTargetPnl() {
70892
71256
  const position2 = this.instance;
@@ -70967,7 +71331,6 @@ class ExchangePosition {
70967
71331
  kelly_confidence_factor: config2.kelly?.kelly_confidence_factor,
70968
71332
  kelly_minimum_risk: config2.kelly?.kelly_minimum_risk,
70969
71333
  kelly_prediction_model: config2.kelly?.kelly_prediction_model,
70970
- kelly_func: config2.kelly?.kelly_func,
70971
71334
  distribution: payload.distribution || config2.distribution,
70972
71335
  distribution_params: payload.distribution_params || config2.distribution_params
70973
71336
  });
@@ -70985,7 +71348,6 @@ class ExchangePosition {
70985
71348
  kelly_confidence_factor: config2.kelly?.kelly_confidence_factor,
70986
71349
  kelly_minimum_risk: config2.kelly?.kelly_minimum_risk,
70987
71350
  kelly_prediction_model: config2.kelly?.kelly_prediction_model,
70988
- kelly_func: config2.kelly?.kelly_func,
70989
71351
  distribution: config2.distribution,
70990
71352
  distribution_params: payload.distribution_params || config2.distribution_params
70991
71353
  }, false);
@@ -71550,8 +71912,8 @@ class ExchangePosition {
71550
71912
  stop,
71551
71913
  risk_reward,
71552
71914
  risk,
71553
- distribution: db_config.distribution,
71554
- distribution_params: db_config.distribution_params
71915
+ distribution: db_config?.distribution,
71916
+ distribution_params: db_config?.distribution_params
71555
71917
  });
71556
71918
  let config2 = generate_config_params(app_config, {
71557
71919
  entry,
@@ -71663,19 +72025,21 @@ class ExchangePosition {
71663
72025
  async buildTrades(payload) {
71664
72026
  const { risk, use_progressive_risk } = payload;
71665
72027
  const config2 = await this.getConfig();
72028
+ const entry = this.kind === "long" ? payload.resistance : payload.support;
72029
+ const stop = this.kind === "long" ? payload.support : payload.resistance;
71666
72030
  const app_config = await this.buildAppConfig({
71667
- entry: config2.entry,
71668
- stop: config2.stop,
71669
- risk_reward: config2.risk_reward,
72031
+ entry: entry || config2.entry,
72032
+ stop: stop || config2.stop,
72033
+ risk_reward: payload.risk_reward || config2.risk_reward,
71670
72034
  risk: risk || config2.risk,
71671
72035
  distribution: config2.distribution,
71672
72036
  distribution_params: config2.distribution_params,
71673
72037
  use_progressive_risk
71674
72038
  });
71675
72039
  const { trades } = await this.placeConfigOrders(app_config, {
71676
- risk_reward: config2.risk_reward,
71677
- entry: config2.entry,
71678
- stop: config2.stop,
72040
+ risk_reward: payload.risk_reward || config2.risk_reward,
72041
+ entry: entry || config2.entry,
72042
+ stop: stop || config2.stop,
71679
72043
  risk_per_trade: risk || config2.risk,
71680
72044
  avg_size: 0,
71681
72045
  neg_pnl: 0,
@@ -71684,7 +72048,6 @@ class ExchangePosition {
71684
72048
  kelly_confidence_factor: config2.kelly?.kelly_confidence_factor,
71685
72049
  kelly_minimum_risk: config2.kelly?.kelly_minimum_risk,
71686
72050
  kelly_prediction_model: config2.kelly?.kelly_prediction_model,
71687
- kelly_func: config2.kelly?.kelly_func,
71688
72051
  distribution: config2.distribution,
71689
72052
  distribution_params: config2.distribution_params,
71690
72053
  use_progressive_risk
@@ -71718,9 +72081,11 @@ class ExchangePosition {
71718
72081
  async tradeConfig(payload) {
71719
72082
  const { override = {} } = payload;
71720
72083
  const config2 = await this.getConfig();
72084
+ const entry = this.kind === "long" ? payload.resistance : payload.support;
72085
+ const stop = this.kind === "long" ? payload.support : payload.resistance;
71721
72086
  const app_config = await this.buildAppConfig({
71722
- entry: config2.entry,
71723
- stop: config2.stop,
72087
+ entry: entry || config2.entry,
72088
+ stop: stop || config2.stop,
71724
72089
  risk_reward: config2.risk_reward,
71725
72090
  risk: config2.risk,
71726
72091
  symbol: this.symbol,
@@ -71728,7 +72093,6 @@ class ExchangePosition {
71728
72093
  kelly_confidence_factor: config2.kelly?.kelly_confidence_factor,
71729
72094
  kelly_minimum_risk: config2.kelly?.kelly_minimum_risk,
71730
72095
  kelly_prediction_model: config2.kelly?.kelly_prediction_model,
71731
- kelly_func: config2.kelly?.kelly_func,
71732
72096
  distribution: config2.distribution,
71733
72097
  distribution_params: config2.distribution_params,
71734
72098
  ...override
@@ -71766,8 +72130,9 @@ class ExchangePosition {
71766
72130
  ratio: payload.ratio
71767
72131
  });
71768
72132
  }
71769
- async getOptimumRiskReward() {
71770
- const app_config = await this.tradeConfig({});
72133
+ async getOptimumRiskReward(payload) {
72134
+ const app_config = await this.tradeConfig(payload || {});
72135
+ console.log("app_config", app_config);
71771
72136
  const risk_reward = computeRiskReward({
71772
72137
  app_config,
71773
72138
  entry: app_config.entry,
@@ -71943,68 +72308,1549 @@ function convert_to_exchange_order(order) {
71943
72308
  };
71944
72309
  }
71945
72310
 
71946
- // src/exchange-account.ts
71947
- class ExchangeAccount {
71948
- instance;
71949
- exchange;
71950
- main_exchange;
71951
- app_db;
71952
- long_position;
71953
- short_position;
71954
- raw_positions;
71955
- constructor(payload, options) {
71956
- this.instance = payload;
71957
- this.exchange = options.exchange;
71958
- this.app_db = options.app_db;
71959
- this.main_exchange = options.main_exchange;
71960
- }
71961
- getDBInstance() {
71962
- return this.app_db;
72311
+ // src/exchanges/paper/index.ts
72312
+ var import_https_proxy_agent3 = __toESM(require_dist2());
72313
+ var import_socks_proxy_agent3 = __toESM(require_dist3());
72314
+
72315
+ // src/exchanges/paper/engine-class.ts
72316
+ function generateSummary2({
72317
+ trades = [],
72318
+ fee_percent = 0.05,
72319
+ anchor
72320
+ }) {
72321
+ if (trades.length === 0) {
72322
+ return {};
71963
72323
  }
71964
- async getLiveExchangeInstance(payload) {
71965
- const symbol_config = await this.recomputeSymbolConfig({
71966
- symbol: payload.symbol,
71967
- refresh: payload.refresh_symbol_config
71968
- });
71969
- const result = await this.app_db.getLiveExchangeInstance({
71970
- account: this.instance,
71971
- symbol: payload.symbol
71972
- });
71973
- if (payload.refresh || !result) {
71974
- const data = await this.exchange.getExchangeAccountInfo({
71975
- account: this.instance,
71976
- symbol: payload.symbol,
71977
- price_places: symbol_config?.price_places,
71978
- decimal_places: symbol_config?.decimal_places
71979
- });
71980
- if (symbol_config?.price_places) {
71981
- data.config.price_places = symbol_config.price_places;
71982
- }
71983
- if (symbol_config?.decimal_places) {
71984
- data.config.decimal_places = symbol_config.decimal_places;
71985
- }
71986
- return await this.app_db.createOrUpdateLiveExchangeInstance({
71987
- account: this.instance,
71988
- symbol: payload.symbol,
71989
- data
71990
- });
72324
+ const avg_entry = trades[0].avg_entry;
72325
+ const avg_size = trades[0].avg_size;
72326
+ const expected_fee = avg_entry * avg_size * fee_percent / 100;
72327
+ return {
72328
+ first_entry: trades.at(-1).entry,
72329
+ last_entry: trades[0].entry,
72330
+ quantity: avg_size,
72331
+ entry: avg_entry,
72332
+ loss: trades[0].neg_pnl,
72333
+ number_of_trades: trades.length,
72334
+ fee: to_f(expected_fee, "%.2f"),
72335
+ anchor_pnl: anchor?.target_pnl
72336
+ };
72337
+ }
72338
+ function cumulativeHelper({
72339
+ tradesList,
72340
+ pos = {
72341
+ quantity: 0,
72342
+ entry: 0
72343
+ },
72344
+ decimal_places = "%.3f"
72345
+ }) {
72346
+ let cumulativeQty = pos.quantity || 0;
72347
+ const reversedTrades = [...tradesList].reverse();
72348
+ const processedTrades = reversedTrades.map((trade) => {
72349
+ cumulativeQty += trade.quantity;
72350
+ const avg_size = to_f(cumulativeQty, decimal_places);
72351
+ return {
72352
+ ...trade,
72353
+ cumulative_size: avg_size
72354
+ };
72355
+ });
72356
+ return processedTrades.reverse();
72357
+ }
72358
+
72359
+ class PositionState {
72360
+ position;
72361
+ trades;
72362
+ take_profit = null;
72363
+ stop_loss = null;
72364
+ entry_fees;
72365
+ fee_percent;
72366
+ global_config;
72367
+ constructor({
72368
+ position: position2,
72369
+ trades,
72370
+ fee_percent = 0,
72371
+ entry_fees = 0,
72372
+ global_config
72373
+ }) {
72374
+ this.position = position2;
72375
+ this.trades = trades ?? [];
72376
+ this.entry_fees = entry_fees ?? 0;
72377
+ this.fee_percent = fee_percent ?? 0;
72378
+ this.global_config = global_config;
72379
+ }
72380
+ get newTrades() {
72381
+ const tradesList = this.trades;
72382
+ const pos = this.position;
72383
+ if (!tradesList || tradesList.length === 0) {
72384
+ return [];
71991
72385
  }
71992
- return result;
71993
- }
71994
- async initializePositions(payload) {
71995
- const raw_positions = await this.syncAccount({
71996
- update: payload.update,
71997
- symbol: payload.symbol,
71998
- live_refresh: payload.update
72386
+ let cumulativeQty = pos.quantity || 0;
72387
+ let cumulativeValue = cumulativeQty * (pos.entry || 0);
72388
+ const reversedTrades = [...tradesList].reverse();
72389
+ const processedTrades = reversedTrades.map((trade) => {
72390
+ cumulativeQty += trade.quantity;
72391
+ cumulativeValue += trade.quantity * trade.entry;
72392
+ const avg_entry = cumulativeQty > 0 ? to_f(cumulativeValue / cumulativeQty, this.global_config.price_places) : 0;
72393
+ const avg_size = to_f(cumulativeQty, this.global_config.decimal_places);
72394
+ const stop = trade.stop || pos.kind === "long" ? Math.min(...tradesList.map((o) => o.entry)) : Math.max(...tradesList.map((o) => o.entry));
72395
+ const neg_pnl = to_f(Math.abs(avg_entry - stop) * avg_size, this.global_config.price_places);
72396
+ return {
72397
+ ...trade,
72398
+ avg_entry,
72399
+ avg_size,
72400
+ neg_pnl
72401
+ };
71999
72402
  });
72000
- const positions = await this.syncAccount({
72001
- symbol: payload.symbol,
72002
- as_view: true
72403
+ return processedTrades.reverse();
72404
+ }
72405
+ generateSummary(fee_percentOverride) {
72406
+ const trades = this.newTrades;
72407
+ const fee_percent = fee_percentOverride ?? this.fee_percent;
72408
+ return {
72409
+ ...generateSummary2({
72410
+ trades,
72411
+ fee_percent
72412
+ }),
72413
+ entry_fees: to_f(this.entry_fees, "%.2f")
72414
+ };
72415
+ }
72416
+ get summary() {
72417
+ return this.generateSummary();
72418
+ }
72419
+ positionAt(price) {
72420
+ const trades = this.newTrades;
72421
+ const kind = this.position.kind;
72422
+ const filtered = trades.filter((trade) => {
72423
+ if (kind === "long") {
72424
+ return trade.entry >= price;
72425
+ }
72426
+ return trade.entry <= price;
72427
+ });
72428
+ return filtered[0];
72429
+ }
72430
+ newPositionState(price) {
72431
+ const position2 = this.position;
72432
+ const trades = this.newTrades;
72433
+ const instance = this.positionAt(price);
72434
+ if (!instance) {
72435
+ return new PositionState({
72436
+ position: { ...position2 },
72437
+ trades,
72438
+ fee_percent: this.fee_percent,
72439
+ entry_fees: this.entry_fees,
72440
+ global_config: this.global_config
72441
+ });
72442
+ }
72443
+ return new PositionState({
72444
+ position: {
72445
+ ...position2,
72446
+ entry: instance.avg_entry,
72447
+ quantity: instance.avg_size
72448
+ },
72449
+ trades,
72450
+ fee_percent: this.fee_percent,
72451
+ entry_fees: this.entry_fees,
72452
+ global_config: this.global_config
72453
+ });
72454
+ }
72455
+ updateTakeProfit({ entry, quantity }) {
72456
+ this.take_profit = {
72457
+ price: entry,
72458
+ quantity: quantity ?? this.position.quantity
72459
+ };
72460
+ }
72461
+ updateStopLoss(payload) {
72462
+ const { stop, quantity } = payload ?? {
72463
+ stop: this.newTrades[0].entry,
72464
+ quantity: this.summary.quantity
72465
+ };
72466
+ this.stop_loss = {
72467
+ price: stop,
72468
+ quantity
72469
+ };
72470
+ }
72471
+ }
72472
+ function determineNewPosition({
72473
+ position: position2,
72474
+ price,
72475
+ trades,
72476
+ global_config
72477
+ }) {
72478
+ const kind = position2.kind;
72479
+ const placed = trades.filter((t2) => {
72480
+ let _price = t2.entry || t2.price;
72481
+ let condition1 = kind === "long" ? _price >= price : _price <= price;
72482
+ return condition1;
72483
+ }).map((t2) => {
72484
+ let _price = t2.entry || t2.price;
72485
+ let condition2 = kind === "long" ? _price >= position2.entry : _price <= position2.entry;
72486
+ if (condition2) {
72487
+ return {
72488
+ ...t2,
72489
+ price: position2.entry,
72490
+ entry: position2.entry
72491
+ };
72492
+ }
72493
+ return t2;
72494
+ });
72495
+ const placeQty = placed.reduce((acc, trade) => acc + trade.quantity, 0);
72496
+ if (position2.quantity > 0) {
72497
+ const placed_less_than_entry = placed.filter((t2) => {
72498
+ let condition1 = kind === "long" ? t2.entry <= position2.entry : t2.entry >= position2.entry;
72499
+ return condition1;
72500
+ });
72501
+ const avg = determine_average_entry_and_size(placed_less_than_entry.map((o) => ({
72502
+ price: o.entry || o.price,
72503
+ quantity: o.quantity
72504
+ })).concat([{ price: position2.entry, quantity: position2.quantity }]), global_config.decimal_places, global_config.price_places);
72505
+ position2.entry = avg.price;
72506
+ position2.quantity = avg.quantity;
72507
+ return position2;
72508
+ }
72509
+ return {
72510
+ ...position2,
72511
+ entry: price,
72512
+ quantity: placeQty
72513
+ };
72514
+ }
72515
+ function positionAt({
72516
+ price,
72517
+ position: position2,
72518
+ trades = [],
72519
+ as_state = true,
72520
+ global_config
72521
+ }) {
72522
+ const kind = position2.kind;
72523
+ const fee_rate = {
72524
+ maker: 0.02,
72525
+ taker: 0.05
72526
+ };
72527
+ const placed = trades.filter((t2) => {
72528
+ let _price = t2.entry || t2.price;
72529
+ let condition1 = kind === "long" ? _price >= price : _price <= price;
72530
+ return condition1;
72531
+ });
72532
+ const not_placed = trades.filter((t2) => {
72533
+ let _price = t2.entry || t2.price;
72534
+ let condition1 = kind === "long" ? _price < price : _price > price;
72535
+ return condition1;
72536
+ });
72537
+ const placeQty = placed.reduce((acc, trade) => acc + trade.quantity, 0);
72538
+ const takerFee = price * placeQty * fee_rate.taker / 100;
72539
+ const makerFee = not_placed.reduce((acc, trade) => acc + trade.entry * trade.quantity * fee_rate.maker / 100, 0);
72540
+ const fee = to_f(takerFee + makerFee, "%.2f");
72541
+ const result = {
72542
+ trades: not_placed,
72543
+ position: determineNewPosition({
72544
+ global_config,
72545
+ position: { ...position2, kind },
72546
+ price,
72547
+ trades
72548
+ }),
72549
+ entry_fees: fee,
72550
+ fee_percent: fee_rate.maker,
72551
+ global_config
72552
+ };
72553
+ if (as_state) {
72554
+ const derivedState = new PositionState({
72555
+ position: result.position,
72556
+ trades: result.trades,
72557
+ fee_percent: result.fee_percent,
72558
+ entry_fees: parseFloat(result.entry_fees) || 0,
72559
+ global_config
72560
+ });
72561
+ return derivedState;
72562
+ }
72563
+ return result;
72564
+ }
72565
+
72566
+ class TradeEngine {
72567
+ trade_details = null;
72568
+ position;
72569
+ global_config;
72570
+ constructor({
72571
+ global_config,
72572
+ position: position2 = { entry: 0, quantity: 0, kind: "long" }
72573
+ }) {
72574
+ this.global_config = global_config;
72575
+ this.position = position2;
72576
+ }
72577
+ generateShortTrades(config2) {
72578
+ const result = compoundAPI.buildWithOptimumReward({
72579
+ global_config: this.global_config,
72580
+ config: config2,
72581
+ settings: config2,
72582
+ use_default: true
72583
+ });
72584
+ this.trade_details = result;
72585
+ }
72586
+ positionAt({
72587
+ price,
72588
+ dangerous,
72589
+ trades: existingTrades
72590
+ }) {
72591
+ const trades = existingTrades || this.trade_details?.trades || [];
72592
+ if (dangerous) {
72593
+ const kind = this.position.kind;
72594
+ const naive_assumptions = positionAt({
72595
+ price,
72596
+ position: {
72597
+ kind,
72598
+ entry: 0,
72599
+ quantity: 0
72600
+ },
72601
+ global_config: this.global_config,
72602
+ trades,
72603
+ as_state: false
72604
+ });
72605
+ const avg_position = determine_average_entry_and_size([
72606
+ {
72607
+ price: this.position.entry,
72608
+ quantity: this.position.quantity
72609
+ },
72610
+ {
72611
+ price: naive_assumptions.position.entry,
72612
+ quantity: naive_assumptions.position.quantity
72613
+ }
72614
+ ], this.global_config.decimal_places, this.global_config.price_places);
72615
+ const new_position = dangerous ? {
72616
+ ...this.position,
72617
+ entry: avg_position.entry,
72618
+ quantity: avg_position.quantity
72619
+ } : this.position;
72620
+ return new PositionState({
72621
+ ...naive_assumptions,
72622
+ trades: naive_assumptions.trades,
72623
+ position: new_position,
72624
+ global_config: this.global_config
72625
+ });
72626
+ }
72627
+ const _trades = cumulativeHelper({
72628
+ tradesList: [...trades].concat({
72629
+ entry: this.position.entry,
72630
+ quantity: this.position.quantity
72631
+ }),
72632
+ decimal_places: this.global_config.decimal_places
72633
+ }).filter((o) => {
72634
+ const numeric = Number(o.cumulative_size);
72635
+ return numeric > this.position.quantity;
72636
+ });
72637
+ return positionAt({
72638
+ price,
72639
+ position: this.position,
72640
+ trades: _trades,
72641
+ global_config: this.global_config
72642
+ });
72643
+ }
72644
+ initializeEngine({
72645
+ price,
72646
+ dangerous,
72647
+ trades,
72648
+ config: config2
72649
+ }) {
72650
+ if (config2) {
72651
+ this.generateShortTrades(config2);
72652
+ }
72653
+ return this.positionAt({
72654
+ price,
72655
+ dangerous,
72656
+ trades
72657
+ });
72658
+ }
72659
+ }
72660
+ function transformTradesToExchangeType({
72661
+ trades,
72662
+ take_profit,
72663
+ stop_loss,
72664
+ kind
72665
+ }) {
72666
+ const currentTime = Date.now();
72667
+ const result = [];
72668
+ trades.forEach((trade, index) => {
72669
+ const side = kind === "long" ? "buy" : "sell";
72670
+ const positionSide = kind.toUpperCase();
72671
+ result.push({
72672
+ avgPrice: "0",
72673
+ clientOrderId: `${index}-${kind}-${side}`.toString(),
72674
+ closePosition: false,
72675
+ cumQuote: "0.0000",
72676
+ executedQty: 0,
72677
+ goodTillDate: 0,
72678
+ id: index,
72679
+ isStop: false,
72680
+ kind,
72681
+ orderId: index,
72682
+ order_id: index,
72683
+ order_type: "future",
72684
+ origQty: trade.quantity,
72685
+ origType: "LIMIT",
72686
+ positionSide,
72687
+ price: trade.entry,
72688
+ priceMatch: "NONE",
72689
+ priceProtect: false,
72690
+ qty: trade.quantity,
72691
+ quantity: trade.quantity,
72692
+ reduceOnly: false,
72693
+ selfTradePreventionMode: "EXPIRE_MAKER",
72694
+ side,
72695
+ status: "NEW",
72696
+ stop: trade.stop || 0,
72697
+ stopPrice: 0,
72698
+ symbol: undefined,
72699
+ time: currentTime,
72700
+ timeInForce: "GTC",
72701
+ triggerPrice: 0,
72702
+ type: "LIMIT",
72703
+ updateTime: currentTime,
72704
+ workingType: "CONTRACT_PRICE"
72705
+ });
72706
+ });
72707
+ let nextOrderId = trades.length;
72708
+ if (take_profit && take_profit.quantity > 0) {
72709
+ const tpSide = kind === "long" ? "sell" : "buy";
72710
+ const tpPositionSide = kind.toUpperCase();
72711
+ result.push({
72712
+ avgPrice: "0",
72713
+ clientOrderId: `tp_${nextOrderId}`,
72714
+ closePosition: false,
72715
+ cumQuote: "0.0000",
72716
+ executedQty: 0,
72717
+ goodTillDate: 0,
72718
+ id: nextOrderId,
72719
+ isStop: false,
72720
+ kind,
72721
+ orderId: nextOrderId,
72722
+ order_id: nextOrderId,
72723
+ order_type: "future",
72724
+ origQty: take_profit.quantity,
72725
+ origType: "LIMIT",
72726
+ positionSide: tpPositionSide,
72727
+ price: take_profit.price,
72728
+ priceMatch: "NONE",
72729
+ priceProtect: false,
72730
+ qty: take_profit.quantity,
72731
+ quantity: take_profit.quantity,
72732
+ reduceOnly: true,
72733
+ selfTradePreventionMode: "EXPIRE_MAKER",
72734
+ side: tpSide,
72735
+ status: "NEW",
72736
+ stop: 0,
72737
+ stopPrice: 0,
72738
+ symbol: undefined,
72739
+ time: currentTime,
72740
+ timeInForce: "GTC",
72741
+ triggerPrice: 0,
72742
+ type: "LIMIT",
72743
+ updateTime: currentTime,
72744
+ workingType: "CONTRACT_PRICE"
72745
+ });
72746
+ nextOrderId++;
72747
+ }
72748
+ if (stop_loss && stop_loss.quantity > 0) {
72749
+ const slSide = kind === "long" ? "sell" : "buy";
72750
+ const slPositionSide = kind.toUpperCase();
72751
+ result.push({
72752
+ avgPrice: "0",
72753
+ clientOrderId: `sl_${nextOrderId}`,
72754
+ closePosition: false,
72755
+ cumQuote: "0.0000",
72756
+ executedQty: 0,
72757
+ goodTillDate: 0,
72758
+ id: nextOrderId,
72759
+ isStop: true,
72760
+ kind,
72761
+ orderId: nextOrderId,
72762
+ order_id: nextOrderId,
72763
+ order_type: "future",
72764
+ origQty: stop_loss.quantity,
72765
+ origType: "LIMIT",
72766
+ positionSide: slPositionSide,
72767
+ price: stop_loss.price,
72768
+ priceMatch: "NONE",
72769
+ priceProtect: false,
72770
+ qty: stop_loss.quantity,
72771
+ quantity: stop_loss.quantity,
72772
+ reduceOnly: true,
72773
+ selfTradePreventionMode: "EXPIRE_MAKER",
72774
+ side: slSide,
72775
+ status: "NEW",
72776
+ stop: stop_loss.price,
72777
+ stopPrice: stop_loss.price,
72778
+ symbol: undefined,
72779
+ time: currentTime,
72780
+ timeInForce: "GTC",
72781
+ triggerPrice: stop_loss.price,
72782
+ type: "LIMIT",
72783
+ updateTime: currentTime,
72784
+ workingType: "CONTRACT_PRICE"
72785
+ });
72786
+ }
72787
+ return result;
72788
+ }
72789
+ function isStopLossOrder(order) {
72790
+ return order.kind === "long" && order.side === "sell" && order.stop > 0 || order.kind === "short" && order.side === "buy" && order.stop > 0;
72791
+ }
72792
+ function isTakeProfitOrder(order) {
72793
+ return order.kind === "long" && order.side === "sell" && order.stop === 0 || order.kind === "short" && order.side === "buy" && order.stop === 0;
72794
+ }
72795
+ function transformExchangeOrderToTradeWithValidation(orders, options) {
72796
+ const {
72797
+ validatePrice = true,
72798
+ validateQuantity = true,
72799
+ defaultStop = 0
72800
+ } = options || {};
72801
+ const trades = [];
72802
+ let take_profit;
72803
+ let stop_loss;
72804
+ orders.forEach((order, index) => {
72805
+ if (validatePrice && (typeof order.price !== "number" || order.price <= 0)) {
72806
+ throw new Error(`Invalid price at index ${index}: ${order.price}. Price must be a positive number`);
72807
+ }
72808
+ if (validateQuantity && (typeof order.quantity !== "number" || order.quantity <= 0)) {
72809
+ throw new Error(`Invalid quantity at index ${index}: ${order.quantity}. Quantity must be a positive number`);
72810
+ }
72811
+ if (order.status !== "NEW") {
72812
+ console.warn(`Order at index ${index} has status "${order.status}" - may not be suitable for trade conversion`);
72813
+ }
72814
+ if (isTakeProfitOrder(order) && !take_profit) {
72815
+ take_profit = {
72816
+ price: order.price,
72817
+ quantity: order.quantity
72818
+ };
72819
+ return;
72820
+ }
72821
+ if (isStopLossOrder(order) && !stop_loss) {
72822
+ stop_loss = {
72823
+ price: order.price,
72824
+ quantity: order.quantity
72825
+ };
72826
+ return;
72827
+ }
72828
+ trades.push({
72829
+ entry: order.price,
72830
+ quantity: order.quantity,
72831
+ avg_size: order.quantity,
72832
+ neg_pnl: 0,
72833
+ avg_entry: order.price,
72834
+ stop: stop_loss?.price || order.stop || defaultStop,
72835
+ reverse_avg_entry: order.price,
72836
+ reverse_avg_quantity: order.quantity,
72837
+ fee: 0,
72838
+ sell_price: undefined
72839
+ });
72840
+ });
72841
+ return {
72842
+ trades,
72843
+ take_profit,
72844
+ stop_loss
72845
+ };
72846
+ }
72847
+ function updatePositionBasedOffTpOrSl({
72848
+ kind,
72849
+ orders,
72850
+ positions,
72851
+ current_price
72852
+ }) {
72853
+ const position2 = positions[kind];
72854
+ const side_orders = orders.filter((o) => o.positionSide.toLowerCase() === kind);
72855
+ const { trades, take_profit, stop_loss } = transformExchangeOrderToTradeWithValidation(side_orders, {
72856
+ validatePrice: false,
72857
+ validateQuantity: false,
72858
+ defaultStop: 0
72859
+ });
72860
+ if (position2.quantity === 0) {
72861
+ return {
72862
+ trades,
72863
+ position: position2,
72864
+ orders: side_orders,
72865
+ changed: null
72866
+ };
72867
+ }
72868
+ if (take_profit && take_profit.quantity) {
72869
+ const should_close = kind === "long" ? current_price >= take_profit.price : current_price <= take_profit.price;
72870
+ if (should_close) {
72871
+ position2.quantity = Math.max(0, position2.quantity - take_profit.quantity);
72872
+ if (position2.quantity === 0) {
72873
+ position2.entry = 0;
72874
+ }
72875
+ return {
72876
+ trades,
72877
+ position: position2,
72878
+ orders: transformTradesToExchangeType({
72879
+ trades,
72880
+ kind,
72881
+ stop_loss
72882
+ }),
72883
+ changed: "tp"
72884
+ };
72885
+ }
72886
+ }
72887
+ if (stop_loss && stop_loss.quantity) {
72888
+ const should_close = kind === "long" ? current_price <= stop_loss.price : current_price >= stop_loss.price;
72889
+ if (should_close) {
72890
+ position2.quantity = Math.max(0, position2.quantity - stop_loss.quantity);
72891
+ if (position2.quantity === 0) {
72892
+ position2.entry = 0;
72893
+ }
72894
+ return {
72895
+ position: position2,
72896
+ orders: transformTradesToExchangeType({
72897
+ trades: trades.filter((t2) => kind === "long" ? t2.entry < stop_loss.price : t2.entry > stop_loss.price),
72898
+ kind,
72899
+ take_profit
72900
+ }),
72901
+ changed: "sl",
72902
+ trades
72903
+ };
72904
+ }
72905
+ }
72906
+ return {
72907
+ trades,
72908
+ position: position2,
72909
+ orders: side_orders,
72910
+ changed: null
72911
+ };
72912
+ }
72913
+
72914
+ // src/exchanges/paper/index.ts
72915
+ async function initClient3(credentials, options) {
72916
+ try {
72917
+ const [account, exchange] = credentials.api_key.split(",");
72918
+ const email = options.db?.email;
72919
+ const password = options.db?.password;
72920
+ async function initializeAppDb() {
72921
+ const db = await initPocketBaseClient({
72922
+ host: options.db?.host,
72923
+ email,
72924
+ password
72925
+ });
72926
+ const app_db2 = new AppDatabase(db, {
72927
+ email: options.db?.email,
72928
+ salt: undefined
72929
+ });
72930
+ return app_db2;
72931
+ }
72932
+ const app_db = await initializeAppDb();
72933
+ const exchange_credentials = await app_db.getCredentials({
72934
+ password
72935
+ });
72936
+ const account_detail = await app_db.get_exchange_db_instance({
72937
+ owner: account,
72938
+ exchange
72939
+ });
72940
+ const { type, ip_address } = account_detail.expand.proxy;
72941
+ const proxyAgent = type === "http" ? new import_https_proxy_agent3.HttpsProxyAgent(`http://${ip_address}`) : new import_socks_proxy_agent3.SocksProxyAgent(`socks5://${ip_address}`);
72942
+ const credential = exchange_credentials.find((c) => c.name === account && c.exchange === exchange);
72943
+ if (!credential) {
72944
+ throw new Error(`Missing API Key or Secret for account '${account}' in .env file. Please check your environment variables.`);
72945
+ }
72946
+ const clientFunc = initClient;
72947
+ const client = await clientFunc(credential, {
72948
+ type: "future",
72949
+ proxyAgent
72950
+ });
72951
+ return {
72952
+ client,
72953
+ app_db,
72954
+ initializeAppDb,
72955
+ owner: account
72956
+ };
72957
+ } catch (e2) {
72958
+ console.log(e2);
72959
+ }
72960
+ }
72961
+ function buildPosition3(position2, orders, options) {
72962
+ const { price_places = "%.1f", decimal_places = "%.3f" } = options;
72963
+ const entry = position2.entryPrice;
72964
+ let quantity = Math.abs(position2.positionAmt);
72965
+ const kind = position2.kind;
72966
+ if (Number.isNaN(quantity)) {
72967
+ quantity = 0;
72968
+ }
72969
+ const limitOrders = orders.filter((x) => x.kind === kind && !x.isStop).filter((o) => {
72970
+ return kind === "long" ? o.side === "buy" : o.side === "sell";
72971
+ });
72972
+ const avg = determine_average_entry_and_size([
72973
+ { price: entry, quantity },
72974
+ ...limitOrders.map((o) => ({ price: o.price, quantity: o.qty }))
72975
+ ], decimal_places, price_places);
72976
+ const stopOrders = orders.filter((x) => x.kind === kind && x.isStop)[0];
72977
+ const tpOrders = orders.filter((x) => x.kind === kind && !x.isStop).filter((x) => x.kind === "long" ? x.side === "sell" : x.side === "buy")[0];
72978
+ const avg_entry = avg.entry;
72979
+ const stop_loss = stopOrders?.triggerPrice || 0;
72980
+ const take_profit = tpOrders?.price || 0;
72981
+ const tp_quantity = tpOrders?.qty || 0;
72982
+ const stop_quantity = stopOrders?.qty || 0;
72983
+ return {
72984
+ id: 0,
72985
+ kind,
72986
+ entry,
72987
+ symbol: position2.symbol,
72988
+ quantity,
72989
+ avg_entry,
72990
+ config_id: 0,
72991
+ stop_loss,
72992
+ take_profit,
72993
+ tp_quantity,
72994
+ stop_quantity,
72995
+ liquidation: position2.liquidationPrice ? to_f(position2.liquidationPrice, price_places) : 0
72996
+ };
72997
+ }
72998
+ var emptyPosition3 = {
72999
+ id: 0,
73000
+ kind: "long",
73001
+ entry: 0,
73002
+ symbol: "",
73003
+ quantity: 0,
73004
+ avg_entry: 0,
73005
+ config_id: 0,
73006
+ stop_loss: 0,
73007
+ take_profit: 0,
73008
+ tp_quantity: 0,
73009
+ stop_quantity: 0
73010
+ };
73011
+ async function getOpenOrders3(client, symbol, type) {
73012
+ const { orders } = await fetchDBExchangeData(client, symbol);
73013
+ if (type === "tp") {
73014
+ const tpOrders = orders.filter((x) => {
73015
+ const isLong = x.positionSide.toLowerCase() === "long";
73016
+ const isStop = x.stopPrice > 0;
73017
+ if (isLong) {
73018
+ return !isStop && x.side.toLowerCase() === "sell" && x.positionSide.toLowerCase() === "long";
73019
+ } else {
73020
+ return !isStop && x.side.toLowerCase() === "buy" && x.positionSide.toLowerCase() === "short";
73021
+ }
73022
+ });
73023
+ return tpOrders.map((x) => ({
73024
+ ...x,
73025
+ price: x.price,
73026
+ qty: x.origQty,
73027
+ kind: x.positionSide.toLowerCase(),
73028
+ side: x.side.toLowerCase(),
73029
+ quantity: x.origQty
73030
+ }));
73031
+ }
73032
+ if (type === "limit") {
73033
+ const limitOrders = orders.filter((x) => {
73034
+ const isLong = x.positionSide.toLowerCase() === "long";
73035
+ const isStop = x.stopPrice > 0;
73036
+ if (isLong) {
73037
+ return !isStop && x.side.toLowerCase() === "buy" && x.positionSide.toLowerCase() === "long";
73038
+ } else {
73039
+ return !isStop && x.side.toLowerCase() === "sell" && x.positionSide.toLowerCase() === "short";
73040
+ }
73041
+ });
73042
+ return limitOrders.map((x) => ({
73043
+ ...x,
73044
+ price: x.price,
73045
+ qty: x.origQty,
73046
+ kind: x.positionSide.toLowerCase(),
73047
+ side: x.side.toLowerCase(),
73048
+ quantity: x.origQty,
73049
+ id: x.orderId,
73050
+ order_id: x.orderId
73051
+ }));
73052
+ }
73053
+ if (type === "stop") {
73054
+ const stopOrders = orders.filter((x) => {
73055
+ const isLong = x.positionSide.toLowerCase() === "long";
73056
+ const isStop = x.stopPrice > 0;
73057
+ if (isLong) {
73058
+ return isStop && x.side.toLowerCase() === "sell" && x.positionSide.toLowerCase() === "long";
73059
+ } else {
73060
+ return isStop && x.side.toLowerCase() === "buy" && x.positionSide.toLowerCase() === "short";
73061
+ }
73062
+ });
73063
+ return stopOrders.map((x) => ({
73064
+ ...x,
73065
+ price: x.price,
73066
+ triggerPrice: x.stopPrice,
73067
+ qty: x.origQty,
73068
+ kind: x.positionSide.toLowerCase(),
73069
+ side: x.side.toLowerCase(),
73070
+ quantity: x.origQty
73071
+ }));
73072
+ }
73073
+ return orders.map((x) => ({
73074
+ ...x,
73075
+ price: x.price,
73076
+ qty: x.origQty,
73077
+ kind: x.positionSide.toLowerCase(),
73078
+ side: x.side.toLowerCase(),
73079
+ isStop: x.stopPrice > 0,
73080
+ order_type: "future",
73081
+ quantity: x.origQty,
73082
+ triggerPrice: x.stopPrice,
73083
+ stop: x.stopPrice,
73084
+ id: x.orderId,
73085
+ order_id: x.orderId
73086
+ }));
73087
+ }
73088
+ async function getWalletBalance3(client, asset = "USDT") {
73089
+ const response = await client.getBalanceV3();
73090
+ const balance = response.find((x) => x.asset.toLowerCase() === asset.toLowerCase())?.balance;
73091
+ return balance ? to_f(balance, "%.2f") : 0;
73092
+ }
73093
+ async function allWalletBalances3(client) {
73094
+ const response = await client.getBalanceV3();
73095
+ return response.map((x) => ({
73096
+ type: "future",
73097
+ asset: x.asset,
73098
+ balance: to_f(x.balance, "%.8f")
73099
+ }));
73100
+ }
73101
+ async function getLeverage2(client, symbol) {
73102
+ const response = await client.getPositions({
73103
+ symbol
73104
+ });
73105
+ const leverage = response[0]?.leverage;
73106
+ return leverage ? to_f(leverage, "%0f") : 0;
73107
+ }
73108
+ async function updateDbDetails(client, payload, current_account_info, new_payload) {
73109
+ const position2 = payload.kind === "long" ? current_account_info.long_position : current_account_info.short_position;
73110
+ const take_profit = new_payload.take_profit || (position2.tp_quantity > 0 ? {
73111
+ price: position2.take_profit,
73112
+ quantity: position2.tp_quantity
73113
+ } : undefined);
73114
+ const stop_loss = new_payload.stop_loss || (position2.stop_quantity > 0 ? {
73115
+ price: position2.stop_loss,
73116
+ quantity: position2.stop_quantity
73117
+ } : undefined);
73118
+ const as_exchange_orders = new_payload.raw ? new_payload.orders : transformTradesToExchangeType({
73119
+ trades: new_payload.orders,
73120
+ kind: payload.kind,
73121
+ take_profit,
73122
+ stop_loss
73123
+ });
73124
+ const reverse_kind = payload.kind === "long" ? "short" : "long";
73125
+ const reverse_orders = current_account_info.config.trades.exchange_info.open_orders.filter((o) => {
73126
+ return o.positionSide.toLowerCase() === reverse_kind;
73127
+ });
73128
+ current_account_info.config.trades.exchange_info.open_orders = as_exchange_orders.concat(reverse_orders);
73129
+ current_account_info.config.trades.exchange_info.positions[payload.kind] = {
73130
+ ...position2,
73131
+ ...new_payload.position
73132
+ };
73133
+ const app_db = await client.initializeAppDb();
73134
+ await app_db.createOrUpdateLiveExchangeInstance({
73135
+ account: {
73136
+ owner: client.owner,
73137
+ exchange: "paper"
73138
+ },
73139
+ symbol: payload.symbol,
73140
+ data: current_account_info
73141
+ });
73142
+ return current_account_info;
73143
+ }
73144
+ async function savePaperDetails(client, payload, new_payload) {
73145
+ const current_account_info = await fetchBinanceAccount2(client, {
73146
+ symbol: payload.symbol
73147
+ }, {
73148
+ price_places: payload.price_places,
73149
+ decimal_places: payload.decimal_places
73150
+ });
73151
+ return await updateDbDetails(client, payload, current_account_info, new_payload);
73152
+ }
73153
+ async function fetchDBExchangeData(client, symbol) {
73154
+ const current_price = await getCurrentPrice3(client.client, symbol);
73155
+ let orders = [];
73156
+ let trades = { long: [], short: [] };
73157
+ let positions = {
73158
+ long: null,
73159
+ short: null
73160
+ };
73161
+ let config2 = {
73162
+ long: null,
73163
+ short: null
73164
+ };
73165
+ const app_db = await client.initializeAppDb();
73166
+ const live_exchange_details = await app_db.getLiveExchangeInstance({
73167
+ account: {
73168
+ owner: client.owner,
73169
+ exchange: "paper"
73170
+ },
73171
+ symbol
73172
+ });
73173
+ const symbol_config = await app_db.getSymbolConfigFromDB(symbol);
73174
+ if (live_exchange_details) {
73175
+ const raw_data = live_exchange_details.data;
73176
+ orders = raw_data.config.trades.exchange_info.open_orders || [];
73177
+ positions = raw_data.config.trades.exchange_info.positions;
73178
+ const long_result = updatePositionBasedOffTpOrSl({
73179
+ current_price,
73180
+ orders,
73181
+ positions,
73182
+ kind: "long"
73183
+ });
73184
+ const short_result = updatePositionBasedOffTpOrSl({
73185
+ current_price,
73186
+ orders,
73187
+ positions,
73188
+ kind: "short"
73189
+ });
73190
+ orders = long_result.orders.concat(short_result.orders);
73191
+ positions.long = long_result.position;
73192
+ positions.short = short_result.position;
73193
+ live_exchange_details.data.config.trades.exchange_info.open_orders = orders;
73194
+ live_exchange_details.data.config.trades.exchange_info.positions = positions;
73195
+ trades = {
73196
+ long: long_result.trades,
73197
+ short: short_result.trades
73198
+ };
73199
+ }
73200
+ return {
73201
+ orders,
73202
+ symbol_config,
73203
+ positions,
73204
+ current_price,
73205
+ config: config2,
73206
+ trades,
73207
+ raw_data: live_exchange_details?.data
73208
+ };
73209
+ }
73210
+ async function getPositionInfo3(client, symbol) {
73211
+ const { positions, symbol_config, current_price, raw_data, trades } = await fetchDBExchangeData(client, symbol);
73212
+ const long_generator = new TradeEngine({
73213
+ position: {
73214
+ kind: "long",
73215
+ entry: positions.long?.entry || 0,
73216
+ quantity: positions.long?.quantity || 0
73217
+ },
73218
+ global_config: symbol_config
73219
+ });
73220
+ const short_generator = new TradeEngine({
73221
+ position: {
73222
+ kind: "short",
73223
+ entry: positions.short?.entry || 0,
73224
+ quantity: positions.short?.quantity || 0
73225
+ },
73226
+ global_config: symbol_config
73227
+ });
73228
+ const long_state = long_generator.initializeEngine({
73229
+ trades: trades.long,
73230
+ price: current_price
73231
+ });
73232
+ const short_state = short_generator.initializeEngine({
73233
+ trades: trades.short,
73234
+ price: current_price
73235
+ });
73236
+ if (raw_data) {
73237
+ const long_state_trades = long_state.newTrades;
73238
+ const short_state_trades = short_state.newTrades;
73239
+ if (trades.long.length !== long_state_trades.length && positions.long.quantity !== long_state.position.quantity) {
73240
+ console.log({
73241
+ old: trades.long.length,
73242
+ new: long_state_trades.length,
73243
+ position: long_state.position
73244
+ });
73245
+ let _position = {
73246
+ entry: long_state.position.entry,
73247
+ quantity: long_state.position.quantity
73248
+ };
73249
+ let tp = undefined;
73250
+ if (long_state_trades.length === 0 && positions.long.quantity === 0) {
73251
+ _position.entry = 0;
73252
+ _position.quantity = 0;
73253
+ long_state.position.entry = 0;
73254
+ long_state.position.quantity = 0;
73255
+ tp = {
73256
+ price: 0,
73257
+ quantity: 0
73258
+ };
73259
+ }
73260
+ await updateDbDetails(client, {
73261
+ kind: "long",
73262
+ symbol
73263
+ }, raw_data, {
73264
+ take_profit: tp,
73265
+ position: _position,
73266
+ orders: long_state_trades
73267
+ });
73268
+ }
73269
+ if (trades.short.length !== short_state_trades.length && positions.short.quantity !== short_state.position.quantity) {
73270
+ let _position = {
73271
+ entry: short_state.position.entry,
73272
+ quantity: short_state.position.quantity
73273
+ };
73274
+ let tp = undefined;
73275
+ if (short_state_trades.length === 0 && positions.short.quantity === 0) {
73276
+ _position.entry = 0;
73277
+ _position.quantity = 0;
73278
+ short_state.position.entry = 0;
73279
+ short_state.position.quantity = 0;
73280
+ tp = {
73281
+ price: 0,
73282
+ quantity: 0
73283
+ };
73284
+ }
73285
+ await updateDbDetails(client, {
73286
+ kind: "short",
73287
+ symbol
73288
+ }, raw_data, {
73289
+ position: _position,
73290
+ orders: short_state_trades,
73291
+ take_profit: tp
73292
+ });
73293
+ }
73294
+ }
73295
+ const long_position = long_state.position;
73296
+ const short_position = short_state.position;
73297
+ const long_quantity = long_position.quantity;
73298
+ const short_quantity = short_position.quantity;
73299
+ const long = {
73300
+ kind: "long",
73301
+ size: long_quantity,
73302
+ positionAmt: long_quantity,
73303
+ entryPrice: long_quantity === 0 ? 0 : long_position.entry,
73304
+ symbol
73305
+ };
73306
+ const short = {
73307
+ kind: "short",
73308
+ size: short_quantity,
73309
+ positionAmt: short_quantity,
73310
+ entryPrice: short_quantity === 0 ? 0 : short_position.entry,
73311
+ symbol
73312
+ };
73313
+ return { long, short };
73314
+ }
73315
+ async function fetchBinanceAccount2(client, json, options) {
73316
+ const [position2, balance, current_price, all_balances, leverage] = await Promise.all([
73317
+ getPositionInfo3(client, json.symbol),
73318
+ getWalletBalance3(client.client, json.symbol.toLowerCase().endsWith("usdt") ? "USDT" : "USDC"),
73319
+ getCurrentPrice3(client.client, json.symbol),
73320
+ allWalletBalances3(client.client),
73321
+ getLeverage2(client.client, json.symbol)
73322
+ ]);
73323
+ const orders = await getOpenOrders3(client, json.symbol);
73324
+ const limitOrders = orders.filter((x) => !x.isStop).filter((o) => {
73325
+ return o.kind === "long" ? o.side === "buy" : o.side === "sell";
73326
+ });
73327
+ const stopOrders = orders.filter((x) => x.isStop);
73328
+ const long_position = buildPosition3(position2.long || {
73329
+ ...emptyPosition3,
73330
+ kind: "long"
73331
+ }, orders, options);
73332
+ const short_position = buildPosition3(position2.short || {
73333
+ ...emptyPosition3,
73334
+ kind: "short"
73335
+ }, orders, options);
73336
+ const result = {
73337
+ id: `${client.owner}-${json.symbol}-paper`,
73338
+ owner: client.owner,
73339
+ config: {
73340
+ symbol: json.symbol,
73341
+ id: 0,
73342
+ price_places: options.price_places,
73343
+ decimal_places: options.decimal_places,
73344
+ trades: {}
73345
+ },
73346
+ exchange: "paper",
73347
+ settings: {
73348
+ default_take_profit: 0,
73349
+ stop_buying: false
73350
+ },
73351
+ balance,
73352
+ long_position,
73353
+ short_position,
73354
+ current_price,
73355
+ orders: limitOrders,
73356
+ entries: [],
73357
+ stop_orders: stopOrders,
73358
+ symbol: json.symbol,
73359
+ leverage
73360
+ };
73361
+ result.config.trades = {
73362
+ exchange_info: {
73363
+ positions: {
73364
+ both: null,
73365
+ long: long_position,
73366
+ short: short_position
73367
+ },
73368
+ account_balances: all_balances,
73369
+ open_orders: orders
73370
+ }
73371
+ };
73372
+ return result;
73373
+ }
73374
+ async function getCurrentPrice3(client, symbol) {
73375
+ const response = await client.getSymbolPriceTickerV2({
73376
+ symbol
73377
+ });
73378
+ return response.price;
73379
+ }
73380
+ async function placeStopOrder3(client, payload) {
73381
+ const {
73382
+ price_places = "%.1f",
73383
+ decimal_places = "%.3f",
73384
+ symbol,
73385
+ current_price,
73386
+ kind,
73387
+ stop,
73388
+ quantity,
73389
+ cancel
73390
+ } = payload;
73391
+ if (kind === "long" && current_price < stop || kind === "short" && current_price > stop) {
73392
+ console.log(`Diverting to TP order for ${symbol} (${kind}). Current: ${current_price}, Stop: ${stop}`);
73393
+ return placeTpOrder3(client, {
73394
+ symbol,
73395
+ tp: stop,
73396
+ quantity,
73397
+ kind,
73398
+ cancel,
73399
+ price_places,
73400
+ decimal_places
73401
+ });
73402
+ }
73403
+ if (payload.cancel) {
73404
+ await cancelAllOrders3(client, symbol, {
73405
+ type: "stop",
73406
+ kind: payload.kind
73407
+ });
73408
+ if (payload.hedge) {
73409
+ const r_kind = payload.kind === "long" ? "short" : "long";
73410
+ await cancelAllOrders3(client, symbol, {
73411
+ type: "stop",
73412
+ kind: r_kind
73413
+ });
73414
+ }
73415
+ }
73416
+ const spread = 1.00005;
73417
+ const order = {
73418
+ kind: payload.kind,
73419
+ side: payload.kind === "long" ? "sell" : "buy",
73420
+ price: payload.kind === "long" ? payload.stop * spread ** -1 : payload.stop * spread,
73421
+ quantity: payload.quantity,
73422
+ stop: payload.final_stop,
73423
+ is_market: !payload.is_limit
73424
+ };
73425
+ if (payload.hedge) {
73426
+ let reverse_kind = payload.kind === "long" ? "short" : "long";
73427
+ order.kind = reverse_kind;
73428
+ order.is_market = false;
73429
+ }
73430
+ const current_account_info = await fetchBinanceAccount2(client, {
73431
+ symbol: payload.symbol
73432
+ }, {
73433
+ price_places: payload.price_places,
73434
+ decimal_places: payload.decimal_places
73435
+ });
73436
+ const position2 = order.kind === "long" ? current_account_info.long_position : current_account_info.short_position;
73437
+ position2.stop_loss = order.stop;
73438
+ position2.stop_quantity = order.quantity;
73439
+ const allOrders = await getOpenOrders3(client, symbol);
73440
+ const sameKindOrders = allOrders.filter((o) => o.positionSide.toLowerCase() === order.kind);
73441
+ const stop_loss = {
73442
+ price: order.stop,
73443
+ quantity: order.quantity
73444
+ };
73445
+ const { trades } = transformExchangeOrderToTradeWithValidation(sameKindOrders, {
73446
+ validateQuantity: false,
73447
+ validatePrice: false
73448
+ });
73449
+ return savePaperDetails(client, {
73450
+ kind: order.kind,
73451
+ symbol: payload.symbol,
73452
+ decimal_places: payload.decimal_places,
73453
+ price_places: payload.price_places
73454
+ }, {
73455
+ position: {},
73456
+ orders: trades,
73457
+ stop_loss
73458
+ });
73459
+ }
73460
+ async function placeTpOrder3(client, payload) {
73461
+ const {
73462
+ symbol,
73463
+ kind,
73464
+ price_places = "%.1f",
73465
+ decimal_places = "%.3f"
73466
+ } = payload;
73467
+ if (payload.cancel) {
73468
+ await cancelAllOrders3(client, symbol, {
73469
+ type: "tp",
73470
+ kind
73471
+ });
73472
+ }
73473
+ let _quantity = payload.quantity;
73474
+ if (!_quantity) {
73475
+ const positionInfo = await getPositionInfo3(client, symbol);
73476
+ _quantity = positionInfo[payload.kind]?.size || 0;
73477
+ }
73478
+ if (_quantity) {
73479
+ const order = {
73480
+ kind: payload.kind,
73481
+ side: payload.kind === "long" ? "sell" : "buy",
73482
+ price: payload.tp,
73483
+ quantity: _quantity
73484
+ };
73485
+ const current_account_info = await fetchBinanceAccount2(client, {
73486
+ symbol: payload.symbol
73487
+ }, {
73488
+ price_places: payload.price_places,
73489
+ decimal_places: payload.decimal_places
73490
+ });
73491
+ const position2 = order.kind === "long" ? current_account_info.long_position : current_account_info.short_position;
73492
+ position2.take_profit = order.price;
73493
+ position2.tp_quantity = order.quantity;
73494
+ const allOrders = await getOpenOrders3(client, symbol);
73495
+ const sameKindOrders = allOrders.filter((o) => o.positionSide.toLowerCase() === order.kind);
73496
+ const take_profit = {
73497
+ price: order.price,
73498
+ quantity: order.quantity
73499
+ };
73500
+ const { trades } = transformExchangeOrderToTradeWithValidation(sameKindOrders, {
73501
+ validateQuantity: false,
73502
+ validatePrice: false
73503
+ });
73504
+ return savePaperDetails(client, {
73505
+ kind: order.kind,
73506
+ symbol: payload.symbol,
73507
+ decimal_places,
73508
+ price_places
73509
+ }, {
73510
+ position: {},
73511
+ orders: trades,
73512
+ take_profit
73513
+ });
73514
+ }
73515
+ return { error: "No quantity" };
73516
+ }
73517
+ async function cancelAllOrders3(client, symbol, payload) {
73518
+ const { type, side, kind } = payload;
73519
+ const {
73520
+ orders: fullOrders,
73521
+ symbol_config,
73522
+ positions
73523
+ } = await fetchDBExchangeData(client, symbol);
73524
+ const allOrders = await getOpenOrders3(client, symbol, type);
73525
+ const sameKindOrders = fullOrders.filter((o) => o.positionSide.toLowerCase() === kind);
73526
+ const ordersToCancel = allOrders.filter((x) => side ? x.side === side : true).filter((x) => kind ? x.kind === kind : true);
73527
+ const excludeFromSameKind = sameKindOrders.filter((o) => !ordersToCancel.map((u) => u.orderId).includes(o.orderId));
73528
+ await savePaperDetails(client, {
73529
+ kind,
73530
+ symbol,
73531
+ decimal_places: symbol_config.decimal_places,
73532
+ price_places: symbol_config.price_places
73533
+ }, {
73534
+ position: positions[kind] || {},
73535
+ orders: excludeFromSameKind,
73536
+ raw: true,
73537
+ stop_loss: {
73538
+ price: 0,
73539
+ quantity: 0
73540
+ }
73541
+ });
73542
+ }
73543
+ async function cancelOrdersParallel2(payload) {
73544
+ const client = payload.custom_client;
73545
+ const { orders: fullOrders, symbol_config } = await fetchDBExchangeData(client, payload.symbol);
73546
+ const excludedOrders = fullOrders.filter((o) => !payload.orders.map((p) => p.clientOrderId || p.orderId).includes(o.clientOrderId));
73547
+ const long_orders = excludedOrders.filter((o) => o.positionSide.toLowerCase() === "long");
73548
+ const short_orders = excludedOrders.filter((o) => o.positionSide.toLowerCase() === "short");
73549
+ await savePaperDetails(client, {
73550
+ kind: "long",
73551
+ symbol: payload.symbol,
73552
+ decimal_places: symbol_config.decimal_places,
73553
+ price_places: symbol_config.price_places
73554
+ }, {
73555
+ position: {},
73556
+ raw: true,
73557
+ orders: long_orders
73558
+ });
73559
+ await savePaperDetails(client, {
73560
+ kind: "short",
73561
+ symbol: payload.symbol,
73562
+ decimal_places: symbol_config.decimal_places,
73563
+ price_places: symbol_config.price_places
73564
+ }, {
73565
+ position: {},
73566
+ raw: true,
73567
+ orders: short_orders
73568
+ });
73569
+ }
73570
+ async function cancelOrders3(payload) {
73571
+ return await cancelOrdersParallel2({
73572
+ symbol: payload.symbol,
73573
+ orders: payload.orders,
73574
+ custom_client: payload.custom_client
73575
+ });
73576
+ }
73577
+ async function createLimitPurchaseOrders2(client, payload) {
73578
+ const { positions, symbol_config, current_price, raw_data } = await fetchDBExchangeData(client, payload.symbol);
73579
+ const kind = payload.orders[0].kind || payload.kind;
73580
+ const params = {
73581
+ position: {
73582
+ kind,
73583
+ entry: positions[kind]?.entry || 0,
73584
+ quantity: positions[kind]?.quantity || 0
73585
+ },
73586
+ global_config: symbol_config
73587
+ };
73588
+ const generator = new TradeEngine(params);
73589
+ const state = generator.initializeEngine({
73590
+ trades: payload.orders,
73591
+ price: current_price
73592
+ });
73593
+ const current_position_state = {
73594
+ entry: state.position.entry,
73595
+ quantity: state.position.quantity
73596
+ };
73597
+ const remaining_orders = state.newTrades;
73598
+ console.log({
73599
+ length: remaining_orders.length,
73600
+ position: current_position_state,
73601
+ last: remaining_orders[remaining_orders.length - 1]
73602
+ });
73603
+ return await updateDbDetails(client, {
73604
+ kind,
73605
+ symbol: payload.symbol
73606
+ }, raw_data, {
73607
+ position: current_position_state,
73608
+ orders: remaining_orders
73609
+ });
73610
+ return await savePaperDetails(client, {
73611
+ kind,
73612
+ symbol: payload.symbol,
73613
+ price_places: payload.price_places,
73614
+ decimal_places: payload.decimal_places
73615
+ }, {
73616
+ position: current_position_state,
73617
+ orders: remaining_orders
73618
+ });
73619
+ }
73620
+
73621
+ class PaperBinanceExchange extends BaseExchange {
73622
+ constructor(client) {
73623
+ super(client);
73624
+ this.name = "paper";
73625
+ this.client = client;
73626
+ }
73627
+ async getPositionInfo(symbol) {
73628
+ return await getPositionInfo3(this.client, symbol);
73629
+ }
73630
+ async getCurrentPrice(symbol) {
73631
+ return await getCurrentPrice3(this.client.client, symbol);
73632
+ }
73633
+ async cancelAllOrders(symbol, payload) {
73634
+ return await cancelAllOrders3(this.client, symbol, payload);
73635
+ }
73636
+ async _createLimitPurchaseOrders(payload) {
73637
+ return await createLimitPurchaseOrders2(this.client, payload);
73638
+ }
73639
+ async analyzeCharts(_payload) {}
73640
+ async getExchangeInfo(options) {
73641
+ return await fetchBinanceAccount2(this.client, {
73642
+ symbol: options.symbol
73643
+ }, {
73644
+ price_places: options.price_places,
73645
+ decimal_places: options.decimal_places
73646
+ });
73647
+ }
73648
+ async _cancelOrders(payload) {
73649
+ return await cancelOrders3({
73650
+ symbol: payload.symbol,
73651
+ orders: payload.orders,
73652
+ custom_client: this.client
73653
+ });
73654
+ }
73655
+ async _placeTpOrder(payload) {
73656
+ return await placeTpOrder3(this.client, payload);
73657
+ }
73658
+ async placeLimitOrders(payload) {
73659
+ const { price_places = "%.1f", decimal_places = "%.3f", symbol } = payload;
73660
+ if (payload.cancel) {
73661
+ await cancelAllOrders3(this.client, symbol, {
73662
+ type: "limit",
73663
+ kind: payload.kind
73664
+ });
73665
+ }
73666
+ if (payload.orders) {
73667
+ const orders = payload.orders.map((x) => ({
73668
+ side: payload.kind === "long" ? "buy" : "sell",
73669
+ price: x.entry,
73670
+ quantity: x.quantity,
73671
+ kind: payload.kind
73672
+ }));
73673
+ return await createLimitPurchaseOrders2(this.client, {
73674
+ symbol,
73675
+ kind: payload.kind,
73676
+ price_places,
73677
+ decimal_places,
73678
+ orders
73679
+ });
73680
+ }
73681
+ }
73682
+ async _placeStopOrder(payload) {
73683
+ return await placeStopOrder3(this.client, payload);
73684
+ }
73685
+ async setLeverage(payload) {
73686
+ return payload.leverage;
73687
+ }
73688
+ async generateConfig(_payload) {
73689
+ return {};
73690
+ }
73691
+ async checkDelistedMovers(payload) {
73692
+ return { movers: [], delisted: [] };
73693
+ }
73694
+ async getAllOpenOrders() {
73695
+ return [];
73696
+ }
73697
+ async getDelistedSpotSymbols() {
73698
+ return [];
73699
+ }
73700
+ async getOpenPositions() {
73701
+ const response = [];
73702
+ return Array.from(new Set(response.map((x) => x.symbol)));
73703
+ }
73704
+ async getOpenOrders(payload) {
73705
+ return await getOpenOrders3(this.client, payload.symbol);
73706
+ }
73707
+ async createLimitPurchaseOrders(payload) {
73708
+ const {
73709
+ orders,
73710
+ kind,
73711
+ decimal_places = "%.3f",
73712
+ price_places = "%.1f",
73713
+ symbol
73714
+ } = payload;
73715
+ const order_payload = orders.map((order) => ({
73716
+ ...order,
73717
+ price: order.entry,
73718
+ kind,
73719
+ side: kind.toLowerCase() === "long" ? "buy" : "sell",
73720
+ stop: undefined
73721
+ }));
73722
+ return await this._createLimitPurchaseOrders({
73723
+ symbol,
73724
+ price_places,
73725
+ decimal_places,
73726
+ orders: order_payload,
73727
+ kind
73728
+ });
73729
+ }
73730
+ async forceClosePosition(symbol, options) {
73731
+ return await forceClosePosition2(this.client, symbol, options);
73732
+ }
73733
+ async getObservableState(_payload) {
73734
+ const { kind } = _payload;
73735
+ const {
73736
+ positions,
73737
+ symbol_config,
73738
+ current_price,
73739
+ orders: _orders,
73740
+ trades,
73741
+ config: config2
73742
+ } = await fetchDBExchangeData(this.client, _payload.symbol);
73743
+ const generator = new TradeEngine({
73744
+ position: {
73745
+ kind,
73746
+ entry: positions[kind]?.entry || 0,
73747
+ quantity: positions[kind]?.quantity || 0
73748
+ },
73749
+ global_config: symbol_config
73750
+ });
73751
+ const long_state = generator.initializeEngine({
73752
+ config: config2[kind],
73753
+ price: current_price,
73754
+ trades: trades.long
73755
+ });
73756
+ return long_state;
73757
+ }
73758
+ async crossAccountTransfer(payload) {}
73759
+ async placeMarketOrder(payload) {
73760
+ return this.forceClosePosition(payload.symbol, {
73761
+ kind: payload.kind,
73762
+ price_places: payload.price_places,
73763
+ decimal_places: payload.decimal_places
73764
+ });
73765
+ }
73766
+ }
73767
+ async function forceClosePosition2(client, symbol, options) {
73768
+ const { price_places = "%.1f", decimal_places = "%.3f" } = options || {};
73769
+ await savePaperDetails(client, {
73770
+ symbol,
73771
+ kind: options.kind,
73772
+ decimal_places,
73773
+ price_places
73774
+ }, {
73775
+ raw: true,
73776
+ position: {
73777
+ entry: 0,
73778
+ quantity: 0
73779
+ },
73780
+ take_profit: {
73781
+ price: 0,
73782
+ quantity: 0
73783
+ },
73784
+ stop_loss: {
73785
+ price: 0,
73786
+ quantity: 0
73787
+ },
73788
+ orders: []
73789
+ });
73790
+ }
73791
+
73792
+ // src/exchange-account.ts
73793
+ class ExchangeAccount {
73794
+ instance;
73795
+ exchange;
73796
+ main_exchange;
73797
+ app_db;
73798
+ long_position;
73799
+ short_position;
73800
+ raw_positions;
73801
+ constructor(payload, options) {
73802
+ this.instance = payload;
73803
+ this.exchange = options.exchange;
73804
+ this.app_db = options.app_db;
73805
+ this.main_exchange = options.main_exchange;
73806
+ }
73807
+ getDBInstance() {
73808
+ return this.app_db;
73809
+ }
73810
+ async getLiveExchangeInstance(payload) {
73811
+ const symbol_config = await this.recomputeSymbolConfig({
73812
+ symbol: payload.symbol,
73813
+ refresh: payload.refresh_symbol_config
73814
+ });
73815
+ const result = await this.app_db.getLiveExchangeInstance({
73816
+ account: this.instance,
73817
+ symbol: payload.symbol
73818
+ });
73819
+ if (payload.refresh || !result) {
73820
+ const data = await this.exchange.getExchangeAccountInfo({
73821
+ account: this.instance,
73822
+ symbol: payload.symbol,
73823
+ price_places: symbol_config?.price_places,
73824
+ decimal_places: symbol_config?.decimal_places
73825
+ });
73826
+ if (symbol_config?.price_places) {
73827
+ data.config.price_places = symbol_config.price_places;
73828
+ }
73829
+ if (symbol_config?.decimal_places) {
73830
+ data.config.decimal_places = symbol_config.decimal_places;
73831
+ }
73832
+ return await this.app_db.createOrUpdateLiveExchangeInstance({
73833
+ account: this.instance,
73834
+ symbol: payload.symbol,
73835
+ data
73836
+ });
73837
+ }
73838
+ return result;
73839
+ }
73840
+ async initializePositions(payload) {
73841
+ const raw_positions = await this.syncAccount({
73842
+ update: payload.update,
73843
+ symbol: payload.symbol,
73844
+ live_refresh: payload.update
73845
+ });
73846
+ const positions = await this.syncAccount({
73847
+ symbol: payload.symbol,
73848
+ as_view: true
72003
73849
  });
72004
73850
  const active_account = await this.getActiveAccount({
72005
73851
  symbol: payload.symbol
72006
73852
  });
72007
- console.log("active_account", payload);
73853
+ console.log("positions", raw_positions);
72008
73854
  const long_position = positions.find((x) => x.kind === "long");
72009
73855
  const short_position = positions.find((x) => x.kind === "short");
72010
73856
  this.long_position = new ExchangePosition({
@@ -72030,7 +73876,7 @@ class ExchangeAccount {
72030
73876
  }
72031
73877
  async getActiveAccount(payload) {
72032
73878
  const { symbol, full = false, refresh = false } = payload;
72033
- const symbol_config = await this.recomputeSymbolConfig({
73879
+ let symbol_config = await this.recomputeSymbolConfig({
72034
73880
  symbol
72035
73881
  });
72036
73882
  const live_exchange_instance = await this.getLiveExchangeInstance({
@@ -72038,6 +73884,8 @@ class ExchangeAccount {
72038
73884
  refresh
72039
73885
  });
72040
73886
  const raw_active_account = live_exchange_instance.data;
73887
+ console.log("raw_active", raw_active_account);
73888
+ console.log("symbol_config", symbol_config);
72041
73889
  const _all = get_active_accounts({
72042
73890
  active_account: raw_active_account,
72043
73891
  symbol_config
@@ -72053,6 +73901,7 @@ class ExchangeAccount {
72053
73901
  refresh: live_refresh,
72054
73902
  symbol
72055
73903
  });
73904
+ console.log("active_accounts", active_account, live_refresh);
72056
73905
  if (leverage) {
72057
73906
  this.exchange.setLeverage({ symbol, leverage });
72058
73907
  }
@@ -72305,7 +74154,7 @@ class ExchangeAccount {
72305
74154
  kind: "short"
72306
74155
  });
72307
74156
  const long_config = await long_position.getConfig();
72308
- const short_config = short_position.getInstance().expand.b_config;
74157
+ const short_config = await short_position.getConfig();
72309
74158
  const config2 = build_reduce_config({
72310
74159
  account: this.instance,
72311
74160
  symbol: payload.symbol,
@@ -73667,9 +75516,9 @@ class ExchangeAccount {
73667
75516
  return profiles;
73668
75517
  }
73669
75518
  }
73670
- function getExchangeKlass(exchange) {
73671
- const func = exchange === "binance" ? BinanceExchange : BybitExchange;
73672
- const clientFunc = exchange === "binance" ? initClient : initClient2;
75519
+ function getExchangeKlass(exchange, dbCredentials) {
75520
+ const func = exchange === "binance" ? BinanceExchange : exchange === "paper" ? PaperBinanceExchange : BybitExchange;
75521
+ const clientFunc = exchange === "binance" ? initClient : exchange === "paper" ? initClient3 : initClient2;
73673
75522
  return async (payload) => {
73674
75523
  const credentials = await payload.getCredentials({
73675
75524
  account: payload.account,
@@ -73677,7 +75526,8 @@ function getExchangeKlass(exchange) {
73677
75526
  });
73678
75527
  const client = await clientFunc(credentials, {
73679
75528
  type: "future",
73680
- proxyAgent: payload.proxyAgent
75529
+ proxyAgent: payload.proxyAgent,
75530
+ db: dbCredentials
73681
75531
  });
73682
75532
  let main_client = null;
73683
75533
  if (exchange === "binance") {
@@ -73703,7 +75553,11 @@ async function getExchangeAccount(payload) {
73703
75553
  const { account, app_db, proxyOptions = {}, canWithdraw = false } = payload;
73704
75554
  const _proxyAgent = await app_db.getProxyForAccount(account);
73705
75555
  const proxyAgent = proxyOptions?.ignore_proxy ? null : proxyOptions?.proxy || _proxyAgent;
73706
- const exchange_instance = await getExchangeKlass(account.exchange)({
75556
+ const exchange_instance = await getExchangeKlass(account.exchange, {
75557
+ host: app_db.pb.baseURL,
75558
+ email: app_db.root_email,
75559
+ password: app_db.root_password
75560
+ })({
73707
75561
  account: account.owner,
73708
75562
  getCredentials: async (_payload) => {
73709
75563
  return await payload.getCredentials({ ..._payload, app_db });
@@ -74113,7 +75967,11 @@ async function initApp(payload) {
74113
75967
  const pb = await initPocketBaseClient(payload.db);
74114
75968
  const app_db = new AppDatabase(pb, {
74115
75969
  email: payload.email,
74116
- salt: payload.salt
75970
+ salt: payload.salt,
75971
+ db: {
75972
+ email: payload.db.email,
75973
+ password: payload.db.password
75974
+ }
74117
75975
  });
74118
75976
  let _getCredentials = payload.getCredentials;
74119
75977
  if (payload.password) {