@gbozee/ultimate 0.0.2-next.23 → 0.0.2-next.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -50646,19 +50646,92 @@ class AppDatabase {
50646
50646
  }
50647
50647
  async deleteAndBulCreateAllOrders(payload) {
50648
50648
  const { account, symbol, all_orders } = payload;
50649
- console.log(`Fetching existing DB orders for ${account.owner}/${account.exchange} - ${symbol} to delete...`);
50650
- let pocketbase_ids_to_delete = [];
50649
+ console.log(`Fetching existing DB orders for ${account.owner}/${account.exchange} - ${symbol} for deduplication...`);
50650
+ let existing_orders = [];
50651
50651
  try {
50652
- const db_orders_to_delete = await this.pb.collection("orders").getFullList({
50652
+ existing_orders = await this.pb.collection("orders").getFullList({
50653
50653
  filter: `symbol:lower="${symbol.toLowerCase()}" && account.owner:lower="${account.owner.toLowerCase()}" && account.exchange:lower="${account.exchange.toLowerCase()}"`
50654
50654
  });
50655
- pocketbase_ids_to_delete = db_orders_to_delete.map((o) => o.id);
50656
- console.log(`Found ${pocketbase_ids_to_delete.length} existing DB orders to delete.`);
50655
+ console.log(`Found ${existing_orders.length} existing DB orders.`);
50657
50656
  } catch (error) {
50658
- console.error("Error fetching DB orders for deletion:", error);
50657
+ console.error("Error fetching existing DB orders:", error);
50658
+ existing_orders = [];
50659
+ }
50660
+ const existing_signatures = new Set;
50661
+ const existing_ids_to_delete = new Set;
50662
+ for (const existing_order of existing_orders) {
50663
+ const signature = [
50664
+ existing_order.symbol?.toLowerCase() || "",
50665
+ existing_order.kind || "",
50666
+ existing_order.price?.toString() || "",
50667
+ existing_order.quantity?.toString() || "",
50668
+ existing_order.side?.toLowerCase() || "",
50669
+ existing_order.stop?.toString() || "0",
50670
+ existing_order.order_id?.toString() || "",
50671
+ existing_order.client_order_id?.toString() || "",
50672
+ account.id
50673
+ ].join("|");
50674
+ existing_signatures.add(signature);
50675
+ existing_ids_to_delete.add(existing_order.id);
50676
+ }
50677
+ const unique_orders = [];
50678
+ const new_signatures = new Set;
50679
+ for (const order of all_orders) {
50680
+ const signature = [
50681
+ symbol.toLowerCase(),
50682
+ order.kind || "",
50683
+ order.price?.toString() || "",
50684
+ order.quantity?.toString() || "",
50685
+ order.side?.toLowerCase() || "",
50686
+ (order.stop || order.triggerPrice || 0).toString(),
50687
+ order.order_id?.toString() || "",
50688
+ (order.clientOrderId || "").toString(),
50689
+ account.id
50690
+ ].join("|");
50691
+ if (!existing_signatures.has(signature) && !new_signatures.has(signature)) {
50692
+ unique_orders.push(order);
50693
+ new_signatures.add(signature);
50694
+ } else {
50695
+ console.log(`Skipping duplicate order: ${order.order_id} for ${symbol}`);
50696
+ }
50697
+ }
50698
+ console.log(`After deduplication: ${unique_orders.length} unique orders out of ${all_orders.length} total orders`);
50699
+ const orders_to_keep = new Set;
50700
+ for (const order of all_orders) {
50701
+ const signature = [
50702
+ symbol.toLowerCase(),
50703
+ order.kind || "",
50704
+ order.price?.toString() || "",
50705
+ order.quantity?.toString() || "",
50706
+ order.side?.toLowerCase() || "",
50707
+ (order.stop || order.triggerPrice || 0).toString(),
50708
+ order.order_id?.toString() || "",
50709
+ (order.clientOrderId || "").toString(),
50710
+ account.id
50711
+ ].join("|");
50712
+ if (existing_signatures.has(signature)) {
50713
+ orders_to_keep.add(signature);
50714
+ }
50715
+ }
50716
+ const pocketbase_ids_to_delete = [];
50717
+ for (const existing_order of existing_orders) {
50718
+ const signature = [
50719
+ existing_order.symbol?.toLowerCase() || "",
50720
+ existing_order.kind || "",
50721
+ existing_order.price?.toString() || "",
50722
+ existing_order.quantity?.toString() || "",
50723
+ existing_order.side?.toLowerCase() || "",
50724
+ existing_order.stop?.toString() || "0",
50725
+ existing_order.order_id?.toString() || "",
50726
+ existing_order.client_order_id?.toString() || "",
50727
+ account.id
50728
+ ].join("|");
50729
+ if (!orders_to_keep.has(signature)) {
50730
+ pocketbase_ids_to_delete.push(existing_order.id);
50731
+ }
50659
50732
  }
50660
50733
  if (pocketbase_ids_to_delete.length > 0) {
50661
- console.log(`Deleting ${pocketbase_ids_to_delete.length} existing orders from PocketBase...`);
50734
+ console.log(`Deleting ${pocketbase_ids_to_delete.length} obsolete orders from PocketBase...`);
50662
50735
  for (let i2 = 0;i2 < pocketbase_ids_to_delete.length; i2 += 5) {
50663
50736
  const batch_to_delete = pocketbase_ids_to_delete.slice(i2, i2 + 5);
50664
50737
  try {
@@ -50667,13 +50740,12 @@ class AppDatabase {
50667
50740
  console.error(`Error deleting batch starting at index ${i2}:`, error, "Batch IDs:", batch_to_delete);
50668
50741
  }
50669
50742
  }
50670
- console.log("Finished deleting existing DB orders.");
50743
+ console.log("Finished deleting obsolete DB orders.");
50671
50744
  }
50672
- console.log(`Found ${all_orders.length} current orders on the exchange.`);
50673
- if (all_orders.length > 0) {
50674
- console.log(`Recreating ${all_orders.length} orders in PocketBase...`);
50675
- for (let i2 = 0;i2 < all_orders.length; i2 += 100) {
50676
- const orderChunk = all_orders.slice(i2, i2 + 100);
50745
+ if (unique_orders.length > 0) {
50746
+ console.log(`Creating ${unique_orders.length} new orders in PocketBase...`);
50747
+ for (let i2 = 0;i2 < unique_orders.length; i2 += 100) {
50748
+ const orderChunk = unique_orders.slice(i2, i2 + 100);
50677
50749
  const batch2 = this.pb.createBatch();
50678
50750
  for (const order of orderChunk) {
50679
50751
  const order_data = {
@@ -50695,8 +50767,9 @@ class AppDatabase {
50695
50767
  console.error(`Error creating batch starting at index ${i2}:`, error);
50696
50768
  }
50697
50769
  }
50698
- console.log("Finished recreating orders in PocketBase.");
50770
+ console.log("Finished creating new orders in PocketBase.");
50699
50771
  }
50772
+ console.log(`Order synchronization complete. Created ${unique_orders.length} new orders, deleted ${pocketbase_ids_to_delete.length} obsolete orders.`);
50700
50773
  }
50701
50774
  async cancelLimitOrders(payload) {
50702
50775
  const { symbol, kind, account, cancelExchangeOrders, raw } = payload;
@@ -59004,1582 +59077,350 @@ function convert_to_exchange_order(order) {
59004
59077
  var import_https_proxy_agent3 = __toESM(require_dist2());
59005
59078
  var import_socks_proxy_agent3 = __toESM(require_dist3());
59006
59079
 
59007
- // node_modules/@legendapp/state/index.mjs
59008
- var hasOwnProperty = Object.prototype.hasOwnProperty;
59009
- function isArray3(obj) {
59010
- return Array.isArray(obj);
59011
- }
59012
- function isObject2(obj) {
59013
- return !!obj && typeof obj === "object" && !isArray3(obj);
59014
- }
59015
- function isFunction(obj) {
59016
- return typeof obj === "function";
59017
- }
59018
- function isPrimitive2(arg) {
59019
- const type = typeof arg;
59020
- return arg !== undefined && (isDate2(arg) || type !== "object" && type !== "function");
59021
- }
59022
- function isDate2(obj) {
59023
- return obj instanceof Date;
59024
- }
59025
- function isBoolean2(obj) {
59026
- return typeof obj === "boolean";
59027
- }
59028
- function isPromise(obj) {
59029
- return obj instanceof Promise;
59030
- }
59031
- function isEmpty(obj) {
59032
- if (!obj)
59033
- return false;
59034
- if (isArray3(obj))
59035
- return obj.length === 0;
59036
- for (const key in obj) {
59037
- if (hasOwnProperty.call(obj, key)) {
59038
- return false;
59039
- }
59040
- }
59041
- return true;
59042
- }
59043
- var setPrimitives = new Set(["boolean", "string", "number"]);
59044
- function isActualPrimitive(arg) {
59045
- return setPrimitives.has(typeof arg);
59046
- }
59047
- function isChildNodeValue(node) {
59048
- return !!node.parent;
59049
- }
59050
- var symbolToPrimitive = Symbol.toPrimitive;
59051
- var symbolGetNode = Symbol("getNode");
59052
- var symbolDelete = /* @__PURE__ */ Symbol("delete");
59053
- var symbolOpaque = Symbol("opaque");
59054
- var optimized = Symbol("optimized");
59055
- var extraPrimitiveActivators = new Map;
59056
- var extraPrimitiveProps = new Map;
59057
- var globalState = {
59058
- isLoadingLocal: false,
59059
- isLoadingRemote: false,
59060
- isMerging: false
59061
- };
59062
- function checkActivate(node) {
59063
- var _a;
59064
- const root = node.root;
59065
- (_a = root.activate) === null || _a === undefined || _a.call(root);
59066
- if (root.toActivate) {
59067
- root.toActivate.forEach(checkActivate);
59068
- delete root.toActivate;
59080
+ // src/exchanges/paper/engine-class.ts
59081
+ function generateSummary2({
59082
+ trades = [],
59083
+ fee_percent = 0.05,
59084
+ anchor
59085
+ }) {
59086
+ if (trades.length === 0) {
59087
+ return {};
59069
59088
  }
59089
+ const avg_entry = trades[0].avg_entry;
59090
+ const avg_size = trades[0].avg_size;
59091
+ const expected_fee = avg_entry * avg_size * fee_percent / 100;
59092
+ return {
59093
+ first_entry: trades.at(-1).entry,
59094
+ last_entry: trades[0].entry,
59095
+ quantity: avg_size,
59096
+ entry: avg_entry,
59097
+ loss: trades[0].neg_pnl,
59098
+ number_of_trades: trades.length,
59099
+ fee: to_f(expected_fee, "%.2f"),
59100
+ anchor_pnl: anchor?.target_pnl
59101
+ };
59070
59102
  }
59071
- function getNode(obs) {
59072
- return obs && obs[symbolGetNode];
59103
+ function cumulativeHelper({
59104
+ tradesList,
59105
+ pos = {
59106
+ quantity: 0,
59107
+ entry: 0
59108
+ },
59109
+ decimal_places = "%.3f"
59110
+ }) {
59111
+ let cumulativeQty = pos.quantity || 0;
59112
+ const reversedTrades = [...tradesList].reverse();
59113
+ const processedTrades = reversedTrades.map((trade) => {
59114
+ cumulativeQty += trade.quantity;
59115
+ const avg_size = to_f(cumulativeQty, decimal_places);
59116
+ return {
59117
+ ...trade,
59118
+ cumulative_size: avg_size
59119
+ };
59120
+ });
59121
+ return processedTrades.reverse();
59073
59122
  }
59074
- function setNodeValue(node, newValue) {
59075
- var _a;
59076
- const parentNode = (_a = node.parent) !== null && _a !== undefined ? _a : node;
59077
- const key = node.parent ? node.key : "_";
59078
- const isDelete = newValue === symbolDelete;
59079
- if (isDelete)
59080
- newValue = undefined;
59081
- const parentValue = node.parent ? ensureNodeValue(parentNode) : parentNode.root;
59082
- const prevValue = parentValue[key];
59083
- const isFunc = isFunction(newValue);
59084
- newValue = !parentNode.isAssigning && isFunc ? newValue(prevValue) : isObject2(newValue) && (newValue === null || newValue === undefined ? undefined : newValue[symbolGetNode]) ? newValue.peek() : newValue;
59085
- try {
59086
- parentNode.isSetting = (parentNode.isSetting || 0) + 1;
59087
- if (isDelete) {
59088
- delete parentValue[key];
59089
- } else {
59090
- parentValue[key] = newValue;
59123
+
59124
+ class PositionState {
59125
+ position;
59126
+ trades;
59127
+ take_profit = null;
59128
+ stop_loss = null;
59129
+ entry_fees;
59130
+ fee_percent;
59131
+ global_config;
59132
+ constructor({
59133
+ position: position2,
59134
+ trades,
59135
+ fee_percent = 0,
59136
+ entry_fees = 0,
59137
+ global_config
59138
+ }) {
59139
+ this.position = position2;
59140
+ this.trades = trades ?? [];
59141
+ this.entry_fees = entry_fees ?? 0;
59142
+ this.fee_percent = fee_percent ?? 0;
59143
+ this.global_config = global_config;
59144
+ }
59145
+ get newTrades() {
59146
+ const tradesList = this.trades;
59147
+ const pos = this.position;
59148
+ if (!tradesList || tradesList.length === 0) {
59149
+ return [];
59091
59150
  }
59092
- } finally {
59093
- parentNode.isSetting--;
59094
- }
59095
- if (parentNode.root.locked && parentNode.root.set) {
59096
- parentNode.root.set(parentNode.root._);
59097
- }
59098
- return { prevValue, newValue, parentValue };
59099
- }
59100
- var arrNodeKeys = [];
59101
- function getNodeValue(node) {
59102
- let count = 0;
59103
- let n2 = node;
59104
- while (isChildNodeValue(n2)) {
59105
- arrNodeKeys[count++] = n2.key;
59106
- n2 = n2.parent;
59107
- }
59108
- let child = node.root._;
59109
- for (let i2 = count - 1;child && i2 >= 0; i2--) {
59110
- const key = arrNodeKeys[i2];
59111
- child = key !== "size" && (child instanceof Map || child instanceof WeakMap) ? child.get(key) : child[key];
59151
+ let cumulativeQty = pos.quantity || 0;
59152
+ let cumulativeValue = cumulativeQty * (pos.entry || 0);
59153
+ const reversedTrades = [...tradesList].reverse();
59154
+ const processedTrades = reversedTrades.map((trade) => {
59155
+ cumulativeQty += trade.quantity;
59156
+ cumulativeValue += trade.quantity * trade.entry;
59157
+ const avg_entry = cumulativeQty > 0 ? to_f(cumulativeValue / cumulativeQty, this.global_config.price_places) : 0;
59158
+ const avg_size = to_f(cumulativeQty, this.global_config.decimal_places);
59159
+ const stop = trade.stop || pos.kind === "long" ? Math.min(...tradesList.map((o) => o.entry)) : Math.max(...tradesList.map((o) => o.entry));
59160
+ const neg_pnl = to_f(Math.abs(avg_entry - stop) * avg_size, this.global_config.price_places);
59161
+ return {
59162
+ ...trade,
59163
+ avg_entry,
59164
+ avg_size,
59165
+ neg_pnl
59166
+ };
59167
+ });
59168
+ return processedTrades.reverse();
59112
59169
  }
59113
- return child;
59114
- }
59115
- function getChildNode(node, key) {
59116
- var _a;
59117
- let child = (_a = node.children) === null || _a === undefined ? undefined : _a.get(key);
59118
- if (!child) {
59119
- child = {
59120
- root: node.root,
59121
- parent: node,
59122
- key,
59123
- lazy: true
59170
+ generateSummary(fee_percentOverride) {
59171
+ const trades = this.newTrades;
59172
+ const fee_percent = fee_percentOverride ?? this.fee_percent;
59173
+ return {
59174
+ ...generateSummary2({
59175
+ trades,
59176
+ fee_percent
59177
+ }),
59178
+ entry_fees: to_f(this.entry_fees, "%.2f")
59124
59179
  };
59125
- if (!node.children) {
59126
- node.children = new Map;
59127
- }
59128
- node.children.set(key, child);
59129
- }
59130
- return child;
59131
- }
59132
- function ensureNodeValue(node) {
59133
- let value2 = getNodeValue(node);
59134
- if (!value2) {
59135
- if (isChildNodeValue(node)) {
59136
- const parent = ensureNodeValue(node.parent);
59137
- value2 = parent[node.key] = {};
59138
- } else {
59139
- value2 = node.root._ = {};
59140
- }
59141
59180
  }
59142
- return value2;
59143
- }
59144
- function findIDKey(obj, node) {
59145
- let idKey = isObject2(obj) ? "id" in obj ? "id" : ("key" in obj) ? "key" : ("_id" in obj) ? "_id" : ("__id" in obj) ? "__id" : undefined : undefined;
59146
- if (!idKey && node.parent) {
59147
- const keyExtractor = getNodeValue(node.parent)[node.key + "_keyExtractor"];
59148
- if (keyExtractor && isFunction(keyExtractor)) {
59149
- idKey = keyExtractor;
59150
- }
59181
+ get summary() {
59182
+ return this.generateSummary();
59151
59183
  }
59152
- return idKey;
59153
- }
59154
- function extractFunction(node, key, fnOrComputed, computedChildNode) {
59155
- if (!node.functions) {
59156
- node.functions = new Map;
59184
+ positionAt(price) {
59185
+ const trades = this.newTrades;
59186
+ const kind = this.position.kind;
59187
+ const filtered = trades.filter((trade) => {
59188
+ if (kind === "long") {
59189
+ return trade.entry >= price;
59190
+ }
59191
+ return trade.entry <= price;
59192
+ });
59193
+ return filtered[0];
59157
59194
  }
59158
- node.functions.set(key, fnOrComputed);
59159
- if (computedChildNode) {
59160
- computedChildNode.parentOther = getChildNode(node, key);
59161
- if (!node.root.toActivate) {
59162
- node.root.toActivate = [];
59195
+ newPositionState(price) {
59196
+ const position2 = this.position;
59197
+ const trades = this.newTrades;
59198
+ const instance = this.positionAt(price);
59199
+ if (!instance) {
59200
+ return new PositionState({
59201
+ position: { ...position2 },
59202
+ trades,
59203
+ fee_percent: this.fee_percent,
59204
+ entry_fees: this.entry_fees,
59205
+ global_config: this.global_config
59206
+ });
59163
59207
  }
59164
- node.root.toActivate.push(computedChildNode);
59165
- }
59166
- }
59167
- function isObservable(obs) {
59168
- return obs && !!obs[symbolGetNode];
59169
- }
59170
- function isEvent(obs) {
59171
- var _a;
59172
- return obs && ((_a = obs[symbolGetNode]) === null || _a === undefined ? undefined : _a.isEvent);
59173
- }
59174
- function computeSelector(selector, e2, retainObservable) {
59175
- let c = selector;
59176
- if (isFunction(c)) {
59177
- c = e2 ? c(e2) : c();
59208
+ return new PositionState({
59209
+ position: {
59210
+ ...position2,
59211
+ entry: instance.avg_entry,
59212
+ quantity: instance.avg_size
59213
+ },
59214
+ trades,
59215
+ fee_percent: this.fee_percent,
59216
+ entry_fees: this.entry_fees,
59217
+ global_config: this.global_config
59218
+ });
59178
59219
  }
59179
- return isObservable(c) && !retainObservable ? c.get() : c;
59180
- }
59181
- function lockObservable(obs, value2) {
59182
- var _a;
59183
- const root = (_a = getNode(obs)) === null || _a === undefined ? undefined : _a.root;
59184
- if (root) {
59185
- root.locked = value2;
59220
+ updateTakeProfit({ entry, quantity }) {
59221
+ this.take_profit = {
59222
+ price: entry,
59223
+ quantity: quantity ?? this.position.quantity
59224
+ };
59186
59225
  }
59187
- }
59188
- function getPathType(value2) {
59189
- return isArray3(value2) ? "array" : value2 instanceof Map ? "map" : value2 instanceof Set ? "set" : "object";
59190
- }
59191
- function replacer(_, value2) {
59192
- if (value2 instanceof Map) {
59193
- return {
59194
- __LSType: "Map",
59195
- value: Array.from(value2.entries())
59226
+ updateStopLoss(payload) {
59227
+ const { stop, quantity } = payload ?? {
59228
+ stop: this.newTrades[0].entry,
59229
+ quantity: this.summary.quantity
59196
59230
  };
59197
- } else if (value2 instanceof Set) {
59198
- return {
59199
- __LSType: "Set",
59200
- value: Array.from(value2)
59231
+ this.stop_loss = {
59232
+ price: stop,
59233
+ quantity
59201
59234
  };
59202
- } else {
59203
- return value2;
59204
- }
59205
- }
59206
- function reviver(_, value2) {
59207
- if (typeof value2 === "object" && value2) {
59208
- if (value2.__LSType === "Map") {
59209
- return new Map(value2.value);
59210
- } else if (value2.__LSType === "Set") {
59211
- return new Set(value2.value);
59212
- }
59213
- }
59214
- return value2;
59215
- }
59216
- function safeStringify(value2) {
59217
- return JSON.stringify(value2, replacer);
59218
- }
59219
- function safeParse(value2) {
59220
- return JSON.parse(value2, reviver);
59221
- }
59222
- function clone2(value2) {
59223
- return safeParse(safeStringify(value2));
59224
- }
59225
- var timeout4;
59226
- var numInBatch = 0;
59227
- var isRunningBatch = false;
59228
- var didDelayEndBatch = false;
59229
- var _afterBatch = [];
59230
- var _queuedBatches = [];
59231
- var _batchMap = new Map;
59232
- function onActionTimeout() {
59233
- if (_batchMap.size > 0) {
59234
- if (true) {
59235
- console.error("Forcibly completing observableBatcher because end() was never called. This may be due to an uncaught error between begin() and end().");
59236
- }
59237
- endBatch(true);
59238
59235
  }
59239
59236
  }
59240
- function isArraySubset(mainArr, subsetArr) {
59241
- for (let i2 = 0;i2 < mainArr.length; i2++) {
59242
- if (mainArr[i2] !== subsetArr[i2]) {
59243
- return false;
59237
+ function determineNewPosition({
59238
+ position: position2,
59239
+ price,
59240
+ trades,
59241
+ global_config
59242
+ }) {
59243
+ const kind = position2.kind;
59244
+ const placed = trades.filter((t2) => {
59245
+ if (kind === "long") {
59246
+ return t2.entry >= price;
59244
59247
  }
59245
- }
59246
- return true;
59247
- }
59248
- function createPreviousHandlerInner(value2, changes) {
59249
- let cloned = value2 ? clone2(value2) : {};
59250
- for (let i2 = 0;i2 < changes.length; i2++) {
59251
- const { path, prevAtPath } = changes[i2];
59252
- let o = cloned;
59253
- if (path.length > 0) {
59254
- let i3;
59255
- for (i3 = 0;i3 < path.length - 1; i3++) {
59256
- o = o[path[i3]];
59257
- }
59258
- const key = path[i3];
59259
- if (o instanceof Map) {
59260
- o.set(key, prevAtPath);
59261
- } else {
59262
- o[key] = prevAtPath;
59248
+ return t2.entry <= price;
59249
+ });
59250
+ const placeQty = placed.reduce((acc, trade) => acc + trade.quantity, 0);
59251
+ if (position2.quantity > 0) {
59252
+ const placed_less_than_entry = placed.filter((t2) => {
59253
+ if (kind === "long") {
59254
+ return t2.entry < position2.entry;
59263
59255
  }
59264
- } else {
59265
- cloned = prevAtPath;
59266
- }
59256
+ return t2.entry > position2.entry;
59257
+ });
59258
+ const avg = determine_average_entry_and_size(placed_less_than_entry.map((o) => ({
59259
+ price: o.entry,
59260
+ quantity: o.quantity
59261
+ })).concat([{ price: position2.entry, quantity: position2.quantity }]), global_config.decimal_places, global_config.price_places);
59262
+ position2.entry = avg.price;
59263
+ position2.quantity = avg.quantity;
59264
+ return position2;
59267
59265
  }
59268
- return cloned;
59269
- }
59270
- function createPreviousHandler(value2, changes) {
59271
- return function() {
59272
- return createPreviousHandlerInner(value2, changes);
59266
+ return {
59267
+ ...position2,
59268
+ entry: price,
59269
+ quantity: placeQty
59273
59270
  };
59274
59271
  }
59275
- function notify(node, value2, prev, level, whenOptimizedOnlyIf) {
59276
- const changesInBatch = new Map;
59277
- computeChangesRecursive(changesInBatch, node, value2, [], [], value2, prev, true, level, whenOptimizedOnlyIf);
59278
- batchNotifyChanges(changesInBatch, true);
59279
- const existing = _batchMap.get(node);
59280
- if (existing) {
59281
- existing.value = value2;
59282
- } else {
59283
- _batchMap.set(node, { value: value2, prev, level, whenOptimizedOnlyIf });
59284
- }
59285
- if (numInBatch <= 0) {
59286
- runBatch();
59287
- }
59288
- }
59289
- function computeChangesAtNode(changesInBatch, node, value2, path, pathTypes, valueAtPath, prevAtPath, immediate, level, whenOptimizedOnlyIf) {
59290
- if (immediate ? node.listenersImmediate : node.listeners) {
59291
- const change = {
59292
- path,
59293
- pathTypes,
59294
- valueAtPath,
59295
- prevAtPath
59296
- };
59297
- const changeInBatch = changesInBatch.get(node);
59298
- if (changeInBatch && path.length > 0) {
59299
- const { changes } = changeInBatch;
59300
- if (!isArraySubset(changes[0].path, change.path)) {
59301
- changes.push(change);
59302
- }
59303
- } else {
59304
- changesInBatch.set(node, {
59305
- level,
59306
- value: value2,
59307
- whenOptimizedOnlyIf,
59308
- changes: [change]
59309
- });
59310
- }
59311
- }
59312
- }
59313
- function computeChangesRecursive(changesInBatch, node, value2, path, pathTypes, valueAtPath, prevAtPath, immediate, level, whenOptimizedOnlyIf) {
59314
- computeChangesAtNode(changesInBatch, node, value2, path, pathTypes, valueAtPath, prevAtPath, immediate, level, whenOptimizedOnlyIf);
59315
- if (node.linkedFromNodes) {
59316
- for (const linkedFromNode of node.linkedFromNodes) {
59317
- computeChangesAtNode(changesInBatch, linkedFromNode, value2, path, pathTypes, valueAtPath, prevAtPath, immediate, level, whenOptimizedOnlyIf);
59318
- }
59319
- }
59320
- if (node.parent) {
59321
- const parent = node.parent;
59322
- if (parent) {
59323
- const parentValue = getNodeValue(parent);
59324
- computeChangesRecursive(changesInBatch, parent, parentValue, [node.key].concat(path), [getPathType(value2)].concat(pathTypes), valueAtPath, prevAtPath, immediate, level + 1, whenOptimizedOnlyIf);
59325
- }
59326
- }
59327
- }
59328
- function batchNotifyChanges(changesInBatch, immediate) {
59329
- const listenersNotified = new Set;
59330
- changesInBatch.forEach(({ changes, level, value: value2, whenOptimizedOnlyIf }, node) => {
59331
- const listeners = immediate ? node.listenersImmediate : node.listeners;
59332
- if (listeners) {
59333
- let listenerParams;
59334
- const arr = Array.from(listeners);
59335
- for (let i2 = 0;i2 < arr.length; i2++) {
59336
- const listenerFn = arr[i2];
59337
- const { track, noArgs, listener } = listenerFn;
59338
- if (!listenersNotified.has(listener)) {
59339
- const ok = track === true ? level <= 0 : track === optimized ? whenOptimizedOnlyIf && level <= 0 : true;
59340
- if (ok) {
59341
- if (!noArgs && !listenerParams) {
59342
- listenerParams = {
59343
- value: value2,
59344
- getPrevious: createPreviousHandler(value2, changes),
59345
- changes
59346
- };
59347
- }
59348
- if (!track) {
59349
- listenersNotified.add(listener);
59350
- }
59351
- listener(listenerParams);
59352
- }
59353
- }
59354
- }
59272
+ function positionAt({
59273
+ price,
59274
+ position: position2,
59275
+ trades = [],
59276
+ as_state = true,
59277
+ global_config
59278
+ }) {
59279
+ const kind = position2.kind;
59280
+ const fee_rate = {
59281
+ maker: 0.02,
59282
+ taker: 0.05
59283
+ };
59284
+ const placed = trades.filter((t2) => {
59285
+ if (kind === "long") {
59286
+ return t2.entry >= price;
59355
59287
  }
59288
+ return t2.entry <= price;
59356
59289
  });
59357
- }
59358
- function runBatch() {
59359
- const map = _batchMap;
59360
- _batchMap = new Map;
59361
- const changesInBatch = new Map;
59362
- map.forEach(({ value: value2, prev, level, whenOptimizedOnlyIf }, node) => {
59363
- computeChangesRecursive(changesInBatch, node, value2, [], [], value2, prev, false, level, whenOptimizedOnlyIf);
59364
- });
59365
- batchNotifyChanges(changesInBatch, false);
59366
- }
59367
- function batch2(fn, onComplete) {
59368
- if (onComplete) {
59369
- if (isRunningBatch) {
59370
- _queuedBatches.push([fn, onComplete]);
59371
- return;
59372
- } else {
59373
- _afterBatch.push(onComplete);
59374
- }
59375
- }
59376
- beginBatch();
59377
- try {
59378
- fn();
59379
- } finally {
59380
- endBatch();
59381
- }
59382
- }
59383
- function beginBatch() {
59384
- numInBatch++;
59385
- if (!timeout4) {
59386
- timeout4 = setTimeout(onActionTimeout, 0);
59387
- }
59388
- }
59389
- function endBatch(force) {
59390
- numInBatch--;
59391
- if (numInBatch <= 0 || force) {
59392
- if (isRunningBatch) {
59393
- didDelayEndBatch = true;
59394
- } else {
59395
- if (timeout4) {
59396
- clearTimeout(timeout4);
59397
- timeout4 = undefined;
59398
- }
59399
- numInBatch = 0;
59400
- const after = _afterBatch;
59401
- if (after.length) {
59402
- _afterBatch = [];
59403
- }
59404
- isRunningBatch = true;
59405
- runBatch();
59406
- isRunningBatch = false;
59407
- for (let i2 = 0;i2 < after.length; i2++) {
59408
- after[i2]();
59409
- }
59410
- if (didDelayEndBatch) {
59411
- didDelayEndBatch = false;
59412
- endBatch(true);
59413
- }
59414
- const queued = _queuedBatches;
59415
- if (queued.length) {
59416
- _queuedBatches = [];
59417
- for (let i2 = 0;i2 < queued.length; i2++) {
59418
- const [fn, onComplete] = queued[i2];
59419
- batch2(fn, onComplete);
59420
- }
59421
- }
59422
- }
59423
- }
59424
- }
59425
- function createObservable(value2, makePrimitive, createObject, createPrimitive) {
59426
- const valueIsPromise = isPromise(value2);
59427
- const root = {
59428
- _: value2
59429
- };
59430
- const node = {
59431
- root,
59432
- lazy: true
59433
- };
59434
- const prim = makePrimitive || isActualPrimitive(value2);
59435
- const obs = prim ? new createPrimitive(node) : createObject(node);
59436
- if (valueIsPromise) {
59437
- extractPromise(node, value2);
59438
- }
59439
- return obs;
59440
- }
59441
- function onChange(node, callback, options = {}) {
59442
- const { initial, immediate, noArgs } = options;
59443
- const { trackingType } = options;
59444
- let listeners = immediate ? node.listenersImmediate : node.listeners;
59445
- if (!listeners) {
59446
- listeners = new Set;
59447
- if (immediate) {
59448
- node.listenersImmediate = listeners;
59449
- } else {
59450
- node.listeners = listeners;
59290
+ const not_placed = trades.filter((t2) => {
59291
+ if (kind === "long") {
59292
+ return t2.entry < price;
59451
59293
  }
59452
- }
59453
- checkActivate(node);
59454
- const listener = {
59455
- listener: callback,
59456
- track: trackingType,
59457
- noArgs
59294
+ return t2.entry > price;
59295
+ });
59296
+ const placeQty = placed.reduce((acc, trade) => acc + trade.quantity, 0);
59297
+ const takerFee = price * placeQty * fee_rate.taker / 100;
59298
+ const makerFee = not_placed.reduce((acc, trade) => acc + trade.entry * trade.quantity * fee_rate.maker / 100, 0);
59299
+ const fee = to_f(takerFee + makerFee, "%.2f");
59300
+ const result = {
59301
+ trades: not_placed,
59302
+ position: determineNewPosition({
59303
+ global_config,
59304
+ position: { ...position2, kind },
59305
+ price,
59306
+ trades
59307
+ }),
59308
+ entry_fees: fee,
59309
+ fee_percent: fee_rate.maker,
59310
+ global_config
59458
59311
  };
59459
- listeners.add(listener);
59460
- if (initial) {
59461
- const value2 = getNodeValue(node);
59462
- callback({
59463
- value: value2,
59464
- changes: [
59465
- {
59466
- path: [],
59467
- pathTypes: [],
59468
- prevAtPath: value2,
59469
- valueAtPath: value2
59470
- }
59471
- ],
59472
- getPrevious: () => {
59473
- return;
59474
- }
59312
+ if (as_state) {
59313
+ const derivedState = new PositionState({
59314
+ position: result.position,
59315
+ trades: result.trades,
59316
+ fee_percent: result.fee_percent,
59317
+ entry_fees: parseFloat(result.entry_fees) || 0,
59318
+ global_config
59475
59319
  });
59320
+ return derivedState;
59476
59321
  }
59477
- return () => listeners.delete(listener);
59478
- }
59479
- var trackCount = 0;
59480
- var trackingQueue = [];
59481
- var tracking = {
59482
- current: undefined
59483
- };
59484
- function beginTracking() {
59485
- trackingQueue.push(tracking.current);
59486
- trackCount++;
59487
- tracking.current = {};
59488
- }
59489
- function endTracking() {
59490
- trackCount--;
59491
- if (trackCount < 0) {
59492
- trackCount = 0;
59493
- }
59494
- tracking.current = trackingQueue.pop();
59495
- }
59496
- function updateTracking(node, track) {
59497
- if (trackCount) {
59498
- const tracker = tracking.current;
59499
- if (tracker) {
59500
- if (!tracker.nodes) {
59501
- tracker.nodes = new Map;
59502
- }
59503
- const existing = tracker.nodes.get(node);
59504
- if (existing) {
59505
- existing.track = existing.track || track;
59506
- existing.num++;
59507
- } else {
59508
- tracker.nodes.set(node, { node, track, num: 1 });
59509
- }
59510
- }
59511
- }
59512
- }
59513
- var ArrayModifiers = new Set([
59514
- "copyWithin",
59515
- "fill",
59516
- "from",
59517
- "pop",
59518
- "push",
59519
- "reverse",
59520
- "shift",
59521
- "sort",
59522
- "splice",
59523
- "unshift"
59524
- ]);
59525
- var ArrayLoopers = new Set([
59526
- "every",
59527
- "filter",
59528
- "find",
59529
- "findIndex",
59530
- "forEach",
59531
- "join",
59532
- "map",
59533
- "some"
59534
- ]);
59535
- var ArrayLoopersReturn = new Set(["filter", "find"]);
59536
- var observableProperties = new Map;
59537
- var observableFns = new Map([
59538
- ["get", get3],
59539
- ["set", set],
59540
- ["peek", peek],
59541
- ["onChange", onChange],
59542
- ["assign", assign],
59543
- ["delete", deleteFn],
59544
- ["toggle", toggle]
59545
- ]);
59546
- if (true) {
59547
- __devUpdateNodes = new Set;
59548
- }
59549
- var __devUpdateNodes;
59550
- function collectionSetter(node, target, prop, ...args) {
59551
- var _a;
59552
- const prevValue = isArray3(target) && target.slice() || target;
59553
- const ret = target[prop].apply(target, args);
59554
- if (node) {
59555
- const hasParent = isChildNodeValue(node);
59556
- const key = hasParent ? node.key : "_";
59557
- const parentValue = hasParent ? getNodeValue(node.parent) : node.root;
59558
- parentValue[key] = prevValue;
59559
- setKey((_a = node.parent) !== null && _a !== undefined ? _a : node, key, target);
59560
- }
59561
- return ret;
59562
- }
59563
- function getKeys(obj, isArr, isMap2) {
59564
- return isArr ? undefined : obj ? isMap2 ? Array.from(obj.keys()) : Object.keys(obj) : [];
59565
- }
59566
- function updateNodes(parent, obj, prevValue) {
59567
- var _a, _b;
59568
- if (typeof __devUpdateNodes !== "undefined" && isObject2(obj)) {
59569
- if (__devUpdateNodes.has(obj)) {
59570
- console.error("[legend-state] Circular reference detected in object. You may want to use opaqueObject to stop traversing child nodes.", obj);
59571
- return false;
59572
- }
59573
- __devUpdateNodes.add(obj);
59574
- }
59575
- if (isObject2(obj) && obj[symbolOpaque] || isObject2(prevValue) && prevValue[symbolOpaque]) {
59576
- const isDiff = obj !== prevValue;
59577
- if (isDiff) {
59578
- if (parent.listeners || parent.listenersImmediate) {
59579
- notify(parent, obj, prevValue, 0);
59580
- }
59581
- }
59582
- if (typeof __devUpdateNodes !== "undefined" && obj !== undefined) {
59583
- __devUpdateNodes.delete(obj);
59584
- }
59585
- return isDiff;
59586
- }
59587
- const isArr = isArray3(obj);
59588
- let prevChildrenById;
59589
- let moved;
59590
- const isMap2 = obj instanceof Map;
59591
- const keys = getKeys(obj, isArr, isMap2);
59592
- const keysPrev = getKeys(prevValue, isArr, isMap2);
59593
- const length = ((_a = keys || obj) === null || _a === undefined ? undefined : _a.length) || 0;
59594
- const lengthPrev = ((_b = keysPrev || prevValue) === null || _b === undefined ? undefined : _b.length) || 0;
59595
- let idField;
59596
- let isIdFieldFunction;
59597
- let hasADiff = false;
59598
- let retValue;
59599
- if (isArr && isArray3(prevValue)) {
59600
- if (prevValue.length > 0) {
59601
- const firstPrevValue = prevValue[0];
59602
- if (firstPrevValue !== undefined) {
59603
- idField = findIDKey(firstPrevValue, parent);
59604
- if (idField) {
59605
- isIdFieldFunction = isFunction(idField);
59606
- prevChildrenById = new Map;
59607
- moved = [];
59608
- const keysSeen = new Set;
59609
- if (parent.children) {
59610
- for (let i2 = 0;i2 < prevValue.length; i2++) {
59611
- const p = prevValue[i2];
59612
- if (p) {
59613
- const child = parent.children.get(i2 + "");
59614
- if (child) {
59615
- const key = isIdFieldFunction ? idField(p) : p[idField];
59616
- if (true) {
59617
- if (keysSeen.has(key)) {
59618
- console.warn(`[legend-state] Warning: Multiple elements in array have the same ID. Key field: ${idField}, Array:`, prevValue);
59619
- }
59620
- keysSeen.add(key);
59621
- }
59622
- prevChildrenById.set(key, child);
59623
- }
59624
- }
59625
- }
59626
- }
59627
- }
59628
- }
59629
- }
59630
- } else if (prevValue && (!obj || isObject2(obj))) {
59631
- const lengthPrev2 = keysPrev.length;
59632
- for (let i2 = 0;i2 < lengthPrev2; i2++) {
59633
- const key = keysPrev[i2];
59634
- if (!keys.includes(key)) {
59635
- hasADiff = true;
59636
- const child = getChildNode(parent, key);
59637
- const prev = isMap2 ? prevValue.get(key) : prevValue[key];
59638
- if (prev !== undefined) {
59639
- if (!isPrimitive2(prev)) {
59640
- updateNodes(child, undefined, prev);
59641
- }
59642
- if (child.listeners || child.listenersImmediate) {
59643
- notify(child, undefined, prev, 0);
59644
- }
59645
- }
59646
- }
59647
- }
59648
- }
59649
- if (obj && !isPrimitive2(obj)) {
59650
- hasADiff = hasADiff || length !== lengthPrev;
59651
- const isArrDiff = hasADiff;
59652
- let didMove = false;
59653
- for (let i2 = 0;i2 < length; i2++) {
59654
- const key = isArr ? i2 + "" : keys[i2];
59655
- const value2 = isMap2 ? obj.get(key) : obj[key];
59656
- const prev = isMap2 ? prevValue === null || prevValue === undefined ? undefined : prevValue.get(key) : prevValue === null || prevValue === undefined ? undefined : prevValue[key];
59657
- let isDiff = value2 !== prev;
59658
- if (isDiff) {
59659
- const id = idField && value2 ? isIdFieldFunction ? idField(value2) : value2[idField] : undefined;
59660
- let child = getChildNode(parent, key);
59661
- if (isArr && id !== undefined) {
59662
- const prevChild = id !== undefined ? prevChildrenById === null || prevChildrenById === undefined ? undefined : prevChildrenById.get(id) : undefined;
59663
- if (!prevChild) {
59664
- isDiff = false;
59665
- hasADiff = true;
59666
- } else if (prevChild !== undefined && prevChild.key !== key) {
59667
- const valuePrevChild = prevValue[prevChild.key];
59668
- if (isArrDiff) {
59669
- child = prevChild;
59670
- parent.children.delete(child.key);
59671
- child.key = key;
59672
- moved.push([key, child]);
59673
- }
59674
- didMove = true;
59675
- isDiff = valuePrevChild !== value2;
59676
- }
59677
- }
59678
- if (isDiff) {
59679
- if (isPrimitive2(value2)) {
59680
- hasADiff = true;
59681
- } else {
59682
- const updatedNodes = updateNodes(child, value2, prev);
59683
- hasADiff = hasADiff || updatedNodes;
59684
- }
59685
- }
59686
- if (isDiff || !isArrDiff) {
59687
- if (child.listeners || child.listenersImmediate) {
59688
- notify(child, value2, prev, 0, !isArrDiff);
59689
- }
59690
- }
59691
- }
59692
- }
59693
- if (moved) {
59694
- for (let i2 = 0;i2 < moved.length; i2++) {
59695
- const [key, child] = moved[i2];
59696
- parent.children.set(key, child);
59697
- }
59698
- }
59699
- retValue = hasADiff || didMove;
59700
- } else if (prevValue !== undefined) {
59701
- retValue = true;
59702
- }
59703
- if (typeof __devUpdateNodes !== "undefined" && obj !== undefined) {
59704
- __devUpdateNodes.delete(obj);
59705
- }
59706
- return retValue !== null && retValue !== undefined ? retValue : false;
59707
- }
59708
- function getProxy(node, p) {
59709
- if (p !== undefined)
59710
- node = getChildNode(node, p);
59711
- return node.proxy || (node.proxy = new Proxy(node, proxyHandler));
59712
- }
59713
- var proxyHandler = {
59714
- get(node, p, receiver) {
59715
- var _a;
59716
- if (p === symbolToPrimitive) {
59717
- throw new Error("[legend-state] observable should not be used as a primitive. You may have forgotten to use .get() or .peek() to get the value of the observable.");
59718
- }
59719
- if (p === symbolGetNode) {
59720
- return node;
59721
- }
59722
- const value2 = peek(node);
59723
- if (node.linkedToNode && p !== "onChange") {
59724
- return proxyHandler.get(node.linkedToNode, p, receiver);
59725
- }
59726
- if (value2 instanceof Map || value2 instanceof WeakMap || value2 instanceof Set || value2 instanceof WeakSet) {
59727
- const ret = handlerMapSet(node, p, value2);
59728
- if (ret !== undefined) {
59729
- return ret;
59730
- }
59731
- }
59732
- const fn = observableFns.get(p);
59733
- if (fn) {
59734
- return function(a, b, c) {
59735
- const l = arguments.length;
59736
- switch (l) {
59737
- case 0:
59738
- return fn(node);
59739
- case 1:
59740
- return fn(node, a);
59741
- case 2:
59742
- return fn(node, a, b);
59743
- default:
59744
- return fn(node, a, b, c);
59745
- }
59746
- };
59747
- }
59748
- if (node.isComputed) {
59749
- if (node.proxyFn && !fn) {
59750
- return node.proxyFn(p);
59751
- } else {
59752
- checkActivate(node);
59753
- }
59754
- }
59755
- const property = observableProperties.get(p);
59756
- if (property) {
59757
- return property.get(node);
59758
- }
59759
- const isValuePrimitive = isPrimitive2(value2);
59760
- if (value2 === undefined || value2 === null || isValuePrimitive) {
59761
- if (extraPrimitiveProps.size && (node.isActivatedPrimitive || extraPrimitiveActivators.has(p))) {
59762
- node.isActivatedPrimitive = true;
59763
- const vPrim = extraPrimitiveProps.get(p);
59764
- if (vPrim !== undefined) {
59765
- return isFunction(vPrim) ? vPrim(getProxy(node)) : vPrim;
59766
- }
59767
- }
59768
- }
59769
- const vProp = value2 === null || value2 === undefined ? undefined : value2[p];
59770
- if (isObject2(value2) && value2[symbolOpaque]) {
59771
- return vProp;
59772
- }
59773
- if (isFunction(vProp)) {
59774
- if (isArray3(value2)) {
59775
- if (ArrayModifiers.has(p)) {
59776
- return (...args) => collectionSetter(node, value2, p, ...args);
59777
- } else if (ArrayLoopers.has(p)) {
59778
- updateTracking(node);
59779
- return function(cbOrig, thisArg) {
59780
- function cbWrapped(_, index, array) {
59781
- return cbOrig(getProxy(node, index + ""), index, array);
59782
- }
59783
- if (ArrayLoopersReturn.has(p)) {
59784
- const isFind = p === "find";
59785
- const out = [];
59786
- for (let i2 = 0;i2 < value2.length; i2++) {
59787
- if (cbWrapped(value2[i2], i2, value2)) {
59788
- const proxy = getProxy(node, i2 + "");
59789
- if (isFind) {
59790
- return proxy;
59791
- } else {
59792
- out.push(proxy);
59793
- }
59794
- }
59795
- }
59796
- return isFind ? undefined : out;
59797
- } else {
59798
- return value2[p](cbWrapped, thisArg);
59799
- }
59800
- };
59801
- }
59802
- }
59803
- return vProp.bind(value2);
59804
- }
59805
- if (isPrimitive2(vProp)) {
59806
- if (isArray3(value2) && p === "length") {
59807
- updateTracking(node, true);
59808
- return vProp;
59809
- }
59810
- }
59811
- const fnOrComputed = (_a = node.functions) === null || _a === undefined ? undefined : _a.get(p);
59812
- if (fnOrComputed) {
59813
- return fnOrComputed;
59814
- }
59815
- if (vProp === undefined && (p === "state" || p === "_state") && node.state) {
59816
- return node.state;
59817
- }
59818
- return getProxy(node, p);
59819
- },
59820
- getPrototypeOf(node) {
59821
- const value2 = getNodeValue(node);
59822
- return value2 !== null && typeof value2 === "object" ? Reflect.getPrototypeOf(value2) : null;
59823
- },
59824
- ownKeys(node) {
59825
- const value2 = getNodeValue(node);
59826
- if (isPrimitive2(value2))
59827
- return [];
59828
- const keys = value2 ? Reflect.ownKeys(value2) : [];
59829
- updateTracking(node, true);
59830
- if (isArray3(value2) && keys[keys.length - 1] === "length") {
59831
- keys.splice(keys.length - 1, 1);
59832
- }
59833
- return keys;
59834
- },
59835
- getOwnPropertyDescriptor(node, prop) {
59836
- const value2 = getNodeValue(node);
59837
- return !isPrimitive2(value2) ? Reflect.getOwnPropertyDescriptor(value2, prop) : undefined;
59838
- },
59839
- set(node, prop, value2) {
59840
- if (node.isSetting) {
59841
- return Reflect.set(node, prop, value2);
59842
- }
59843
- if (node.isAssigning) {
59844
- setKey(node, prop, value2);
59845
- return true;
59846
- }
59847
- const property = observableProperties.get(prop);
59848
- if (property) {
59849
- property.set(node, value2);
59850
- return true;
59851
- }
59852
- if (true) {
59853
- console.warn("[legend-state]: Error: Cannot set a value directly:", prop, value2);
59854
- }
59855
- return false;
59856
- },
59857
- deleteProperty(node, prop) {
59858
- if (node.isSetting) {
59859
- return Reflect.deleteProperty(node, prop);
59860
- } else {
59861
- if (true) {
59862
- console.warn("[legend-state]: Error: Cannot delete a value directly:", prop);
59863
- }
59864
- return false;
59865
- }
59866
- },
59867
- has(node, prop) {
59868
- const value2 = getNodeValue(node);
59869
- return Reflect.has(value2, prop);
59870
- }
59871
- };
59872
- function set(node, newValue) {
59873
- if (node.parent) {
59874
- return setKey(node.parent, node.key, newValue);
59875
- } else {
59876
- return setKey(node, "_", newValue);
59877
- }
59878
- }
59879
- function toggle(node) {
59880
- const value2 = getNodeValue(node);
59881
- if (value2 === undefined || isBoolean2(value2)) {
59882
- set(node, !value2);
59883
- return !value2;
59884
- } else if (true) {
59885
- throw new Error("[legend-state] Cannot toggle a non-boolean value");
59886
- }
59887
- }
59888
- function setKey(node, key, newValue, level) {
59889
- if (true) {
59890
- if (typeof HTMLElement !== "undefined" && newValue instanceof HTMLElement) {
59891
- console.warn(`[legend-state] Set an HTMLElement into state. You probably don't want to do that.`);
59892
- }
59893
- }
59894
- if (node.root.locked && !node.root.set) {
59895
- if (globalState.isMerging) {
59896
- return;
59897
- } else {
59898
- throw new Error("[legend-state] Cannot modify an observable while it is locked. Please make sure that you unlock the observable before making changes.");
59899
- }
59900
- }
59901
- const isRoot = !node.parent && key === "_";
59902
- const childNode = isRoot ? node : getChildNode(node, key);
59903
- const { newValue: savedValue, prevValue, parentValue } = setNodeValue(childNode, newValue);
59904
- const isFunc = isFunction(savedValue);
59905
- const isPrim = isPrimitive2(savedValue) || savedValue instanceof Date;
59906
- if (savedValue !== prevValue) {
59907
- updateNodesAndNotify(node, savedValue, prevValue, childNode, isPrim, isRoot, level);
59908
- }
59909
- extractFunctionOrComputed(node, parentValue, key, savedValue);
59910
- return isFunc ? savedValue : isRoot ? getProxy(node) : getProxy(node, key);
59911
- }
59912
- function assign(node, value2) {
59913
- const proxy = getProxy(node);
59914
- beginBatch();
59915
- if (isPrimitive2(node.root._)) {
59916
- node.root._ = {};
59917
- }
59918
- node.isAssigning = (node.isAssigning || 0) + 1;
59919
- try {
59920
- Object.assign(proxy, value2);
59921
- } finally {
59922
- node.isAssigning--;
59923
- }
59924
- endBatch();
59925
- return proxy;
59926
- }
59927
- function deleteFn(node, key) {
59928
- if (key === undefined && isChildNodeValue(node)) {
59929
- key = node.key;
59930
- node = node.parent;
59931
- }
59932
- setKey(node, key !== null && key !== undefined ? key : "_", symbolDelete, -1);
59933
- }
59934
- function handlerMapSet(node, p, value2) {
59935
- const vProp = value2 === null || value2 === undefined ? undefined : value2[p];
59936
- if (p === "size") {
59937
- return getProxy(node, p);
59938
- } else if (isFunction(vProp)) {
59939
- return function(a, b, c) {
59940
- const l = arguments.length;
59941
- const valueMap = value2;
59942
- if (p === "get") {
59943
- if (l > 0 && typeof a !== "boolean" && a !== optimized) {
59944
- return getProxy(node, a);
59945
- }
59946
- } else if (p === "set") {
59947
- if (l === 2) {
59948
- const prev = valueMap.get(a);
59949
- const ret = valueMap.set(a, b);
59950
- if (prev !== b) {
59951
- updateNodesAndNotify(getChildNode(node, a), b, prev);
59952
- }
59953
- return ret;
59954
- }
59955
- } else if (p === "delete") {
59956
- if (l > 0) {
59957
- const prev = value2.get ? valueMap.get(a) : a;
59958
- const ret = value2.delete(a);
59959
- if (ret) {
59960
- updateNodesAndNotify(getChildNode(node, a), undefined, prev);
59961
- }
59962
- return ret;
59963
- }
59964
- } else if (p === "clear") {
59965
- const prev = new Map(valueMap);
59966
- const size = valueMap.size;
59967
- valueMap.clear();
59968
- if (size) {
59969
- updateNodesAndNotify(node, value2, prev);
59970
- }
59971
- return;
59972
- } else if (p === "add") {
59973
- const prev = new Set(value2);
59974
- const ret = value2.add(a);
59975
- if (!value2.has(p)) {
59976
- notify(node, ret, prev, 0);
59977
- }
59978
- return ret;
59979
- }
59980
- const fn = observableFns.get(p);
59981
- if (fn) {
59982
- switch (l) {
59983
- case 0:
59984
- return fn(node);
59985
- case 1:
59986
- return fn(node, a);
59987
- case 2:
59988
- return fn(node, a, b);
59989
- default:
59990
- return fn(node, a, b, c);
59991
- }
59992
- } else {
59993
- return value2[p](a, b);
59994
- }
59995
- };
59996
- }
59997
- }
59998
- function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRoot, level) {
59999
- if (!childNode)
60000
- childNode = node;
60001
- beginBatch();
60002
- if (isPrim === undefined) {
60003
- isPrim = isPrimitive2(newValue);
60004
- }
60005
- let hasADiff = isPrim;
60006
- let whenOptimizedOnlyIf = false;
60007
- if (!isPrim || prevValue && !isPrimitive2(prevValue)) {
60008
- if (typeof __devUpdateNodes !== "undefined") {
60009
- __devUpdateNodes.clear();
60010
- }
60011
- hasADiff = updateNodes(childNode, newValue, prevValue);
60012
- if (isArray3(newValue)) {
60013
- whenOptimizedOnlyIf = (newValue === null || newValue === undefined ? undefined : newValue.length) !== (prevValue === null || prevValue === undefined ? undefined : prevValue.length);
60014
- }
60015
- }
60016
- if (isPrim || !newValue || isEmpty(newValue) && !isEmpty(prevValue) ? newValue !== prevValue : hasADiff) {
60017
- notify(isPrim && isRoot ? node : childNode, newValue, prevValue, (level !== null && level !== undefined ? level : prevValue === undefined) ? -1 : hasADiff ? 0 : 1, whenOptimizedOnlyIf);
60018
- }
60019
- endBatch();
60020
- }
60021
- function extractPromise(node, value2) {
60022
- if (!node.state) {
60023
- node.state = createObservable({
60024
- isLoaded: false
60025
- }, false, getProxy);
60026
- }
60027
- value2.then((value3) => {
60028
- set(node, value3);
60029
- node.state.isLoaded.set(true);
60030
- }).catch((error) => {
60031
- node.state.error.set(error);
60032
- });
60033
- }
60034
- function extractFunctionOrComputed(node, obj, k, v) {
60035
- if (isPromise(v)) {
60036
- extractPromise(getChildNode(node, k), v);
60037
- } else if (typeof v === "function") {
60038
- extractFunction(node, k, v);
60039
- } else if (typeof v == "object" && v !== null && v !== undefined) {
60040
- const childNode = getNode(v);
60041
- if (childNode === null || childNode === undefined ? undefined : childNode.isComputed) {
60042
- extractFunction(node, k, v, childNode);
60043
- delete obj[k];
60044
- } else {
60045
- return true;
60046
- }
60047
- }
60048
- }
60049
- function get3(node, options) {
60050
- const track = options ? isObject2(options) ? options.shallow : options : undefined;
60051
- updateTracking(node, track);
60052
- return peek(node);
60053
- }
60054
- function peek(node) {
60055
- const value2 = getNodeValue(node);
60056
- if (node.lazy) {
60057
- delete node.lazy;
60058
- for (const key in value2) {
60059
- if (hasOwnProperty.call(value2, key)) {
60060
- extractFunctionOrComputed(node, value2, key, value2[key]);
60061
- }
60062
- }
60063
- }
60064
- checkActivate(node);
60065
- return value2;
60066
- }
60067
- var fns = ["get", "set", "peek", "onChange", "toggle"];
60068
- function ObservablePrimitiveClass(node) {
60069
- this._node = node;
60070
- for (let i2 = 0;i2 < fns.length; i2++) {
60071
- const key = fns[i2];
60072
- this[key] = this[key].bind(this);
60073
- }
60074
- }
60075
- function proto(key, fn) {
60076
- ObservablePrimitiveClass.prototype[key] = function(...args) {
60077
- return fn.call(this, this._node, ...args);
60078
- };
60079
- }
60080
- proto("peek", peek);
60081
- proto("get", get3);
60082
- proto("set", set);
60083
- proto("onChange", onChange);
60084
- Object.defineProperty(ObservablePrimitiveClass.prototype, symbolGetNode, {
60085
- configurable: true,
60086
- get() {
60087
- return this._node;
60088
- }
60089
- });
60090
- ObservablePrimitiveClass.prototype.toggle = function() {
60091
- const value2 = this.peek();
60092
- if (value2 === undefined || isBoolean2(value2)) {
60093
- this.set(!value2);
60094
- } else if (true) {
60095
- throw new Error("[legend-state] Cannot toggle a non-boolean value");
60096
- }
60097
- return !value2;
60098
- };
60099
- ObservablePrimitiveClass.prototype.delete = function() {
60100
- this.set(undefined);
60101
- return this;
60102
- };
60103
- function observable(value2) {
60104
- return createObservable(value2, false, getProxy, ObservablePrimitiveClass);
60105
- }
60106
- function setupTracking(nodes, update, noArgs, immediate) {
60107
- let listeners = [];
60108
- nodes === null || nodes === undefined || nodes.forEach((tracked) => {
60109
- const { node, track } = tracked;
60110
- listeners.push(onChange(node, update, { trackingType: track, immediate, noArgs }));
60111
- });
60112
- return () => {
60113
- if (listeners) {
60114
- for (let i2 = 0;i2 < listeners.length; i2++) {
60115
- listeners[i2]();
60116
- }
60117
- listeners = undefined;
60118
- }
60119
- };
60120
- }
60121
- function trackSelector(selector, update, observeEvent, observeOptions, createResubscribe) {
60122
- var _a;
60123
- let dispose;
60124
- let resubscribe;
60125
- let updateFn = update;
60126
- beginTracking();
60127
- const value2 = selector ? computeSelector(selector, observeEvent, observeOptions === null || observeOptions === undefined ? undefined : observeOptions.fromComputed) : selector;
60128
- const tracker = tracking.current;
60129
- const nodes = tracker.nodes;
60130
- endTracking();
60131
- if (tracker && nodes) {
60132
- (_a = tracker.traceListeners) === null || _a === undefined || _a.call(tracker, nodes);
60133
- if (tracker.traceUpdates) {
60134
- updateFn = tracker.traceUpdates(update);
60135
- }
60136
- tracker.traceListeners = undefined;
60137
- tracker.traceUpdates = undefined;
60138
- }
60139
- if (!(observeEvent === null || observeEvent === undefined ? undefined : observeEvent.cancel)) {
60140
- dispose = setupTracking(nodes, updateFn, false, observeOptions === null || observeOptions === undefined ? undefined : observeOptions.immediate);
60141
- if (true) {
60142
- resubscribe = createResubscribe ? () => {
60143
- dispose === null || dispose === undefined || dispose();
60144
- dispose = setupTracking(nodes, updateFn);
60145
- return dispose;
60146
- } : undefined;
60147
- }
60148
- }
60149
- return { value: value2, dispose, resubscribe };
60150
- }
60151
- function observe(selectorOrRun, reactionOrOptions, options) {
60152
- let reaction;
60153
- if (isFunction(reactionOrOptions)) {
60154
- reaction = reactionOrOptions;
60155
- } else {
60156
- options = reactionOrOptions;
60157
- }
60158
- let dispose;
60159
- const e2 = { num: 0 };
60160
- const update = function() {
60161
- if (e2.onCleanup) {
60162
- e2.onCleanup();
60163
- e2.onCleanup = undefined;
60164
- }
60165
- beginBatch();
60166
- delete e2.value;
60167
- dispose === null || dispose === undefined || dispose();
60168
- const { dispose: _dispose, value: value2 } = trackSelector(selectorOrRun, update, e2, options);
60169
- dispose = _dispose;
60170
- e2.value = value2;
60171
- if (e2.onCleanupReaction) {
60172
- e2.onCleanupReaction();
60173
- e2.onCleanupReaction = undefined;
60174
- }
60175
- endBatch();
60176
- if (reaction && (e2.num > 0 || !isEvent(selectorOrRun)) && (e2.previous !== e2.value || (options === null || options === undefined ? undefined : options.fromComputed) || typeof e2.value === "object")) {
60177
- reaction(e2);
60178
- }
60179
- e2.previous = e2.value;
60180
- e2.num++;
60181
- };
60182
- update();
60183
- return () => {
60184
- var _a, _b;
60185
- (_a = e2.onCleanup) === null || _a === undefined || _a.call(e2);
60186
- e2.onCleanup = undefined;
60187
- (_b = e2.onCleanupReaction) === null || _b === undefined || _b.call(e2);
60188
- e2.onCleanupReaction = undefined;
60189
- dispose === null || dispose === undefined || dispose();
60190
- };
60191
- }
60192
- function computed(compute, set$1) {
60193
- const obs = observable();
60194
- lockObservable(obs, true);
60195
- const node = getNode(obs);
60196
- node.isComputed = true;
60197
- let isSetAfterActivated = false;
60198
- const setInner = function(val) {
60199
- const prevNode = node.linkedToNode;
60200
- if (prevNode) {
60201
- prevNode.linkedFromNodes.delete(node);
60202
- node.linkedToNode = undefined;
60203
- }
60204
- const { parentOther } = node;
60205
- if (isObservable(val)) {
60206
- const linkedNode = getNode(val);
60207
- node.linkedToNode = linkedNode;
60208
- if (!linkedNode.linkedFromNodes) {
60209
- linkedNode.linkedFromNodes = new Set;
60210
- }
60211
- linkedNode.linkedFromNodes.add(node);
60212
- if (node.parentOther) {
60213
- onChange(linkedNode, ({ value: value2 }) => {
60214
- setNodeValue(node.parentOther, value2);
60215
- }, { initial: true });
60216
- }
60217
- if (prevNode) {
60218
- const value2 = getNodeValue(linkedNode);
60219
- const prevValue = getNodeValue(prevNode);
60220
- notify(node, value2, prevValue, 0);
60221
- }
60222
- } else if (val !== obs.peek()) {
60223
- lockObservable(obs, false);
60224
- const setter = isSetAfterActivated ? set : setNodeValue;
60225
- setter(node, val);
60226
- if (parentOther) {
60227
- let didUnlock = false;
60228
- if (parentOther.root.locked) {
60229
- parentOther.root.locked = false;
60230
- didUnlock = true;
60231
- }
60232
- setter(parentOther, val);
60233
- if (didUnlock) {
60234
- parentOther.root.locked = true;
60235
- }
60236
- }
60237
- lockObservable(obs, true);
60238
- } else if (parentOther) {
60239
- setNodeValue(parentOther, val);
60240
- }
60241
- isSetAfterActivated = true;
60242
- };
60243
- node.root.activate = () => {
60244
- node.root.activate = undefined;
60245
- observe(compute, ({ value: value2 }) => {
60246
- if (isPromise(value2)) {
60247
- value2.then((v) => setInner(v));
60248
- } else {
60249
- setInner(value2);
60250
- }
60251
- }, { immediate: true, fromComputed: true });
60252
- };
60253
- if (set$1) {
60254
- node.root.set = (value2) => {
60255
- batch2(() => set$1(value2));
60256
- };
60257
- }
60258
- return obs;
59322
+ return result;
60259
59323
  }
60260
59324
 
60261
- // src/exchanges/paper/engine.ts
60262
- function generateSummary2({
60263
- trades = [],
60264
- fee_percent = 0.05,
60265
- anchor
60266
- }) {
60267
- if (trades.length === 0) {
60268
- return {};
59325
+ class TradeEngine {
59326
+ trade_details = null;
59327
+ position;
59328
+ global_config;
59329
+ constructor({
59330
+ global_config,
59331
+ position: position2 = { entry: 0, quantity: 0, kind: "long" }
59332
+ }) {
59333
+ this.global_config = global_config;
59334
+ this.position = position2;
60269
59335
  }
60270
- const avg_entry = trades[0].avg_entry;
60271
- const avg_size = trades[0].avg_size;
60272
- const expected_fee = avg_entry * avg_size * fee_percent / 100;
60273
- return {
60274
- first_entry: trades.at(-1).entry,
60275
- last_entry: trades[0].entry,
60276
- quantity: avg_size,
60277
- entry: avg_entry,
60278
- loss: trades[0].neg_pnl,
60279
- number_of_trades: trades.length,
60280
- fee: to_f(expected_fee, "%.2f"),
60281
- anchor_pnl: anchor?.target_pnl
60282
- };
60283
- }
60284
- function cumulativeHelper({
60285
- tradesList,
60286
- pos = {
60287
- quantity: 0,
60288
- entry: 0
60289
- },
60290
- decimal_places = "%.3f"
60291
- }) {
60292
- let cumulativeQty = pos.quantity || 0;
60293
- const reversedTrades = [...tradesList].reverse();
60294
- const processedTrades = reversedTrades.map((trade) => {
60295
- cumulativeQty += trade.quantity;
60296
- const avg_size = to_f(cumulativeQty, decimal_places);
60297
- return {
60298
- ...trade,
60299
- cumulative_size: avg_size
60300
- };
60301
- });
60302
- return processedTrades.reverse();
60303
- }
60304
- function preComputePositionState({
60305
- position: position2,
60306
- trades,
60307
- fee_percent = 0,
60308
- entry_fees = 0,
60309
- global_config: symbol_config
60310
- }) {
60311
- const state = observable({
60312
- position: position2,
60313
- trades,
60314
- take_profit: null,
60315
- stop_loss: null,
60316
- newTrades: () => {
60317
- const tradesList = state.trades.get();
60318
- const pos = state.position.get();
60319
- if (!tradesList || tradesList.length === 0) {
60320
- return [];
60321
- }
60322
- let cumulativeQty = pos.quantity || 0;
60323
- let cumulativeValue = cumulativeQty * (pos.entry || 0);
60324
- const reversedTrades = [...tradesList].reverse();
60325
- const processedTrades = reversedTrades.map((trade) => {
60326
- cumulativeQty += trade.quantity;
60327
- cumulativeValue += trade.quantity * trade.entry;
60328
- const avg_entry = cumulativeQty > 0 ? to_f(cumulativeValue / cumulativeQty, symbol_config.price_places) : 0;
60329
- const avg_size = to_f(cumulativeQty, symbol_config.decimal_places);
60330
- const stop = trade.stop || pos.kind === "long" ? Math.min(...tradesList.map((o) => o.entry)) : Math.max(...tradesList.map((o) => o.entry));
60331
- const neg_pnl = to_f(Math.abs(avg_entry - stop) * avg_size, symbol_config.price_places);
60332
- return {
60333
- ...trade,
60334
- avg_entry,
60335
- avg_size,
60336
- neg_pnl
60337
- };
60338
- });
60339
- return processedTrades.reverse();
60340
- },
60341
- generateSummary() {
60342
- const trades2 = state.newTrades();
60343
- return {
60344
- ...generateSummary2({
60345
- trades: trades2,
60346
- fee_percent
60347
- }),
60348
- entry_fees: to_f(entry_fees, "%.2f")
60349
- };
60350
- },
60351
- summary: computed(() => {
60352
- const trades2 = state.newTrades();
60353
- return {
60354
- ...generateSummary2({
60355
- trades: trades2,
60356
- fee_percent
60357
- }),
60358
- entry_fees: to_f(entry_fees, "%.2f")
60359
- };
60360
- }),
60361
- positionAt(price) {
60362
- const trades2 = state.newTrades();
60363
- const kind = state.position.get().kind;
60364
- const filtered = trades2.filter((trade) => {
60365
- if (kind === "long") {
60366
- return trade.entry >= price;
60367
- }
60368
- return trade.entry <= price;
60369
- });
60370
- return filtered[0];
60371
- },
60372
- newPositionState(price) {
60373
- const position3 = state.position.get();
60374
- const trades2 = state.newTrades();
60375
- const instance = state.positionAt(price);
60376
- return positionAt({
59336
+ generateShortTrades(config2) {
59337
+ const result = compoundAPI.buildWithOptimumReward({
59338
+ global_config: this.global_config,
59339
+ config: config2,
59340
+ settings: config2,
59341
+ use_default: true
59342
+ });
59343
+ this.trade_details = result;
59344
+ }
59345
+ positionAt({
59346
+ price,
59347
+ dangerous,
59348
+ trades: existingTrades
59349
+ }) {
59350
+ const trades = existingTrades || this.trade_details?.trades || [];
59351
+ if (dangerous) {
59352
+ const kind = this.position.kind;
59353
+ const naive_assumptions = positionAt({
60377
59354
  price,
60378
59355
  position: {
60379
- ...position3,
60380
- entry: instance.avg_entry,
60381
- quantity: instance.avg_size
59356
+ kind,
59357
+ entry: 0,
59358
+ quantity: 0
60382
59359
  },
60383
- trades: trades2,
60384
- global_config: symbol_config
60385
- });
60386
- },
60387
- updateTakeProfit({
60388
- entry,
60389
- quantity
60390
- }) {
60391
- state.take_profit.set({
60392
- price: entry,
60393
- quantity: quantity || state.position.get().quantity
60394
- });
60395
- },
60396
- updateStopLoss(payload) {
60397
- const { stop, quantity } = payload || {
60398
- stop: state.newTrades()[0].entry,
60399
- quantity: state.summary.get().quantity
60400
- };
60401
- state.stop_loss.set({
60402
- price: stop,
60403
- quantity
59360
+ global_config: this.global_config,
59361
+ trades,
59362
+ as_state: false
60404
59363
  });
60405
- }
60406
- });
60407
- return state;
60408
- }
60409
- function determineNewPosition({
60410
- position: position2,
60411
- price,
60412
- trades,
60413
- global_config
60414
- }) {
60415
- const kind = position2.kind;
60416
- const placed = trades.filter((t2) => {
60417
- if (kind === "long") {
60418
- return t2.entry >= price;
60419
- }
60420
- return t2.entry <= price;
60421
- });
60422
- const placeQty = placed.reduce((acc, trade) => acc + trade.quantity, 0);
60423
- if (position2.quantity > 0) {
60424
- const placed_less_than_entry = placed.filter((t2) => {
60425
- if (kind === "long") {
60426
- return t2.entry < position2.entry;
60427
- }
60428
- return t2.entry > position2.entry;
59364
+ const avg_position = determine_average_entry_and_size([
59365
+ {
59366
+ price: this.position.entry,
59367
+ quantity: this.position.quantity
59368
+ },
59369
+ {
59370
+ price: naive_assumptions.position.entry,
59371
+ quantity: naive_assumptions.position.quantity
59372
+ }
59373
+ ], this.global_config.decimal_places, this.global_config.price_places);
59374
+ const new_position = dangerous ? {
59375
+ ...this.position,
59376
+ entry: avg_position.entry,
59377
+ quantity: avg_position.quantity
59378
+ } : this.position;
59379
+ return new PositionState({
59380
+ ...naive_assumptions,
59381
+ trades: naive_assumptions.trades,
59382
+ position: new_position,
59383
+ global_config: this.global_config
59384
+ });
59385
+ }
59386
+ const _trades = cumulativeHelper({
59387
+ tradesList: [...trades].concat({
59388
+ entry: this.position.entry,
59389
+ quantity: this.position.quantity
59390
+ }),
59391
+ decimal_places: this.global_config.decimal_places
59392
+ }).filter((o) => {
59393
+ const numeric = Number(o.cumulative_size);
59394
+ return numeric > this.position.quantity;
59395
+ });
59396
+ return positionAt({
59397
+ price,
59398
+ position: this.position,
59399
+ trades: _trades,
59400
+ global_config: this.global_config
60429
59401
  });
60430
- const avg = determine_average_entry_and_size(placed_less_than_entry.map((o) => ({
60431
- price: o.entry,
60432
- quantity: o.quantity
60433
- })).concat([{ price: position2.entry, quantity: position2.quantity }]), global_config.decimal_places, global_config.price_places);
60434
- position2.entry = avg.price;
60435
- position2.quantity = avg.quantity;
60436
- return position2;
60437
59402
  }
60438
- return {
60439
- ...position2,
60440
- entry: price,
60441
- quantity: placeQty
60442
- };
60443
- }
60444
- function positionAt({
60445
- price,
60446
- position: position2,
60447
- trades = [],
60448
- as_state = true,
60449
- global_config
60450
- }) {
60451
- const kind = position2.kind;
60452
- const fee_rate = {
60453
- maker: 0.02,
60454
- taker: 0.05
60455
- };
60456
- const placed = trades.filter((t2) => {
60457
- if (kind === "long") {
60458
- return t2.entry >= price;
60459
- }
60460
- return t2.entry <= price;
60461
- });
60462
- const not_placed = trades.filter((t2) => {
60463
- if (kind === "long") {
60464
- return t2.entry < price;
59403
+ initializeEngine({
59404
+ price,
59405
+ dangerous,
59406
+ trades,
59407
+ config: config2
59408
+ }) {
59409
+ if (config2) {
59410
+ this.generateShortTrades(config2);
60465
59411
  }
60466
- return t2.entry > price;
60467
- });
60468
- const placeQty = placed.reduce((acc, trade) => acc + trade.quantity, 0);
60469
- const takerFee = price * placeQty * fee_rate.taker / 100;
60470
- const makerFee = not_placed.reduce((acc, trade) => acc + trade.entry * trade.quantity * fee_rate.maker / 100, 0);
60471
- const fee = to_f(takerFee + makerFee, "%.2f");
60472
- const result = {
60473
- trades: not_placed,
60474
- position: determineNewPosition({
60475
- global_config,
60476
- position: { ...position2, kind },
59412
+ return this.positionAt({
60477
59413
  price,
59414
+ dangerous,
60478
59415
  trades
60479
- }),
60480
- entry_fees: fee,
60481
- fee_percent: fee_rate.maker,
60482
- global_config
60483
- };
60484
- if (as_state) {
60485
- const derivedState = preComputePositionState(result);
60486
- return derivedState;
59416
+ });
60487
59417
  }
60488
- return result;
60489
- }
60490
- function primitiveTradeGenerator({
60491
- global_config,
60492
- position: position2 = { entry: 0, quantity: 0, kind: "long" }
60493
- }) {
60494
- const state = observable({
60495
- trade_details: null,
60496
- async generateShortTrades(config2) {
60497
- const result = compoundAPI.buildWithOptimumReward({
60498
- global_config,
60499
- config: config2,
60500
- settings: config2,
60501
- use_default: true
60502
- });
60503
- state.trade_details.set(result);
60504
- },
60505
- async generateAllTrades(config2) {
60506
- await Promise.allSettled([state.generateShortTrades(config2)]);
60507
- },
60508
- positionAt({
60509
- price,
60510
- dangerous,
60511
- trades: existingTrades
60512
- }) {
60513
- const trades = existingTrades || state.trade_details.get()?.trades || [];
60514
- if (dangerous) {
60515
- const kind = position2.kind;
60516
- const naive_assumptions = positionAt({
60517
- price,
60518
- position: {
60519
- kind,
60520
- entry: 0,
60521
- quantity: 0
60522
- },
60523
- global_config,
60524
- trades,
60525
- as_state: false
60526
- });
60527
- const avg_position = determine_average_entry_and_size([
60528
- {
60529
- price: position2.entry,
60530
- quantity: position2.quantity
60531
- },
60532
- {
60533
- price: naive_assumptions.position.entry,
60534
- quantity: naive_assumptions.position.quantity
60535
- }
60536
- ], global_config.decimal_places, global_config.price_places);
60537
- const new_position = dangerous ? {
60538
- ...position2,
60539
- entry: avg_position.entry,
60540
- quantity: avg_position.quantity
60541
- } : position2;
60542
- return preComputePositionState({
60543
- ...naive_assumptions,
60544
- trades: naive_assumptions.trades,
60545
- position: new_position,
60546
- global_config
60547
- });
60548
- }
60549
- const _trades = existingTrades || cumulativeHelper({
60550
- tradesList: trades,
60551
- decimal_places: global_config.decimal_places
60552
- }).filter((o) => o.cumulative_size > position2.quantity);
60553
- return positionAt({
60554
- price,
60555
- position: position2,
60556
- trades: _trades,
60557
- global_config
60558
- });
60559
- },
60560
- async initializeEngine({
60561
- price,
60562
- dangerous,
60563
- trades,
60564
- config: config2
60565
- }) {
60566
- if (config2) {
60567
- await state.generateAllTrades(config2);
60568
- }
60569
- return state.positionAt({
60570
- price,
60571
- dangerous,
60572
- trades
60573
- });
60574
- }
60575
- });
60576
- return state;
60577
59418
  }
60578
59419
  function transformTradesToExchangeType({
60579
59420
  trades,
60580
- kind,
60581
59421
  take_profit,
60582
- stop_loss
59422
+ stop_loss,
59423
+ kind
60583
59424
  }) {
60584
59425
  const currentTime = Date.now();
60585
59426
  const result = [];
@@ -60623,7 +59464,7 @@ function transformTradesToExchangeType({
60623
59464
  });
60624
59465
  });
60625
59466
  let nextOrderId = trades.length;
60626
- if (take_profit) {
59467
+ if (take_profit && take_profit.quantity > 0) {
60627
59468
  const tpSide = kind === "long" ? "sell" : "buy";
60628
59469
  const tpPositionSide = kind.toUpperCase();
60629
59470
  result.push({
@@ -60663,7 +59504,7 @@ function transformTradesToExchangeType({
60663
59504
  });
60664
59505
  nextOrderId++;
60665
59506
  }
60666
- if (stop_loss) {
59507
+ if (stop_loss && stop_loss.quantity > 0) {
60667
59508
  const slSide = kind === "long" ? "sell" : "buy";
60668
59509
  const slPositionSide = kind.toUpperCase();
60669
59510
  result.push({
@@ -60712,8 +59553,8 @@ function isTakeProfitOrder(order) {
60712
59553
  }
60713
59554
  function transformExchangeOrderToTradeWithValidation(orders, options) {
60714
59555
  const {
60715
- validateQuantity = true,
60716
59556
  validatePrice = true,
59557
+ validateQuantity = true,
60717
59558
  defaultStop = 0
60718
59559
  } = options || {};
60719
59560
  const trades = [];
@@ -60762,6 +59603,72 @@ function transformExchangeOrderToTradeWithValidation(orders, options) {
60762
59603
  stop_loss
60763
59604
  };
60764
59605
  }
59606
+ function updatePositionBasedOffTpOrSl({
59607
+ kind,
59608
+ orders,
59609
+ positions,
59610
+ current_price
59611
+ }) {
59612
+ const position2 = positions[kind];
59613
+ const side_orders = orders.filter((o) => o.positionSide.toLowerCase() === kind);
59614
+ const { trades, take_profit, stop_loss } = transformExchangeOrderToTradeWithValidation(side_orders, {
59615
+ validatePrice: false,
59616
+ validateQuantity: false,
59617
+ defaultStop: 0
59618
+ });
59619
+ if (position2.quantity === 0) {
59620
+ return {
59621
+ trades,
59622
+ position: position2,
59623
+ orders: side_orders,
59624
+ changed: null
59625
+ };
59626
+ }
59627
+ if (take_profit && take_profit.quantity) {
59628
+ const should_close = kind === "long" ? current_price >= take_profit.price : current_price <= take_profit.price;
59629
+ if (should_close) {
59630
+ position2.quantity = Math.max(0, position2.quantity - take_profit.quantity);
59631
+ if (position2.quantity === 0) {
59632
+ position2.entry = 0;
59633
+ }
59634
+ return {
59635
+ trades,
59636
+ position: position2,
59637
+ orders: transformTradesToExchangeType({
59638
+ trades,
59639
+ kind,
59640
+ stop_loss
59641
+ }),
59642
+ changed: "tp"
59643
+ };
59644
+ }
59645
+ }
59646
+ if (stop_loss && stop_loss.quantity) {
59647
+ const should_close = kind === "long" ? current_price <= stop_loss.price : current_price >= stop_loss.price;
59648
+ if (should_close) {
59649
+ position2.quantity = Math.max(0, position2.quantity - stop_loss.quantity);
59650
+ if (position2.quantity === 0) {
59651
+ position2.entry = 0;
59652
+ }
59653
+ return {
59654
+ position: position2,
59655
+ orders: transformTradesToExchangeType({
59656
+ trades: trades.filter((t2) => kind === "long" ? t2.entry < stop_loss.price : t2.entry > stop_loss.price),
59657
+ kind,
59658
+ take_profit
59659
+ }),
59660
+ changed: "sl",
59661
+ trades
59662
+ };
59663
+ }
59664
+ }
59665
+ return {
59666
+ trades,
59667
+ position: position2,
59668
+ orders: side_orders,
59669
+ changed: null
59670
+ };
59671
+ }
60765
59672
 
60766
59673
  // src/exchanges/paper/index.ts
60767
59674
  async function initClient3(credentials, options) {
@@ -60998,6 +59905,7 @@ async function savePaperDetails(client, payload, new_payload) {
60998
59905
  async function fetchDBExchangeData(client, symbol) {
60999
59906
  const current_price = await getCurrentPrice3(client.client, symbol);
61000
59907
  let orders = [];
59908
+ let trades = { long: [], short: [] };
61001
59909
  let positions = {
61002
59910
  long: null,
61003
59911
  short: null
@@ -61040,6 +59948,30 @@ async function fetchDBExchangeData(client, symbol) {
61040
59948
  const raw_data = live_exchange_details.data;
61041
59949
  orders = raw_data.config.trades.exchange_info.open_orders || [];
61042
59950
  positions = raw_data.config.trades.exchange_info.positions;
59951
+ const long_result = updatePositionBasedOffTpOrSl({
59952
+ current_price,
59953
+ orders,
59954
+ positions,
59955
+ kind: "long"
59956
+ });
59957
+ const short_result = updatePositionBasedOffTpOrSl({
59958
+ current_price,
59959
+ orders,
59960
+ positions,
59961
+ kind: "short"
59962
+ });
59963
+ orders = long_result.orders.concat(short_result.orders);
59964
+ positions.long = long_result.position;
59965
+ positions.short = short_result.position;
59966
+ if (["tp", "sl"].includes(long_result.changed)) {
59967
+ config2.long = null;
59968
+ }
59969
+ live_exchange_details.data.config.trades.exchange_info.open_orders = orders;
59970
+ live_exchange_details.data.config.trades.exchange_info.positions = positions;
59971
+ trades = {
59972
+ long: long_result.trades,
59973
+ short: short_result.trades
59974
+ };
61043
59975
  }
61044
59976
  return {
61045
59977
  orders,
@@ -61047,12 +59979,13 @@ async function fetchDBExchangeData(client, symbol) {
61047
59979
  positions,
61048
59980
  current_price,
61049
59981
  config: config2,
59982
+ trades,
61050
59983
  raw_data: live_exchange_details?.data
61051
59984
  };
61052
59985
  }
61053
59986
  async function getPositionInfo3(client, symbol) {
61054
- const { positions, symbol_config, current_price, orders, config: config2, raw_data } = await fetchDBExchangeData(client, symbol);
61055
- const long_generator = primitiveTradeGenerator({
59987
+ const { positions, symbol_config, current_price, raw_data, trades } = await fetchDBExchangeData(client, symbol);
59988
+ const long_generator = new TradeEngine({
61056
59989
  position: {
61057
59990
  kind: "long",
61058
59991
  entry: positions.long?.entry || 0,
@@ -61060,7 +59993,7 @@ async function getPositionInfo3(client, symbol) {
61060
59993
  },
61061
59994
  global_config: symbol_config
61062
59995
  });
61063
- const short_generator = primitiveTradeGenerator({
59996
+ const short_generator = new TradeEngine({
61064
59997
  position: {
61065
59998
  kind: "short",
61066
59999
  entry: positions.short?.entry || 0,
@@ -61068,36 +60001,75 @@ async function getPositionInfo3(client, symbol) {
61068
60001
  },
61069
60002
  global_config: symbol_config
61070
60003
  });
61071
- const long_trades = transformExchangeOrderToTradeWithValidation(orders.filter((o) => o.positionSide.toLowerCase() === "long"), { validatePrice: false, validateQuantity: false });
61072
- const short_trades = transformExchangeOrderToTradeWithValidation(orders.filter((o) => o.positionSide.toLowerCase() === "short"), { validatePrice: false, validateQuantity: false });
61073
- const long_state = await long_generator.initializeEngine({
61074
- trades: config2.long ? undefined : long_trades.trades,
61075
- config: config2.long,
60004
+ const long_state = long_generator.initializeEngine({
60005
+ trades: trades.long,
61076
60006
  price: current_price
61077
60007
  });
61078
- const short_state = await short_generator.initializeEngine({
61079
- trades: config2.short ? undefined : short_trades.trades,
61080
- config: config2.short,
60008
+ const short_state = short_generator.initializeEngine({
60009
+ trades: trades.short,
61081
60010
  price: current_price
61082
60011
  });
61083
60012
  if (raw_data) {
61084
- await updateDbDetails(client, {
61085
- kind: "long",
61086
- symbol
61087
- }, raw_data, {
61088
- position: {},
61089
- orders: long_state.newTrades()
61090
- });
61091
- await updateDbDetails(client, {
61092
- kind: "short",
61093
- symbol
61094
- }, raw_data, {
61095
- position: {},
61096
- orders: short_state.newTrades()
61097
- });
60013
+ const long_state_trades = long_state.newTrades;
60014
+ const short_state_trades = short_state.newTrades;
60015
+ if (trades.long.length !== long_state_trades.length && positions.long.quantity !== long_state.position.quantity) {
60016
+ console.log({
60017
+ old: trades.long.length,
60018
+ new: long_state_trades.length,
60019
+ position: long_state.position
60020
+ });
60021
+ let _position = {
60022
+ entry: long_state.position.entry,
60023
+ quantity: long_state.position.quantity
60024
+ };
60025
+ let tp = undefined;
60026
+ if (long_state_trades.length === 0 && positions.long.quantity === 0) {
60027
+ _position.entry = 0;
60028
+ _position.quantity = 0;
60029
+ long_state.position.entry = 0;
60030
+ long_state.position.quantity = 0;
60031
+ tp = {
60032
+ price: 0,
60033
+ quantity: 0
60034
+ };
60035
+ }
60036
+ await updateDbDetails(client, {
60037
+ kind: "long",
60038
+ symbol
60039
+ }, raw_data, {
60040
+ take_profit: tp,
60041
+ position: _position,
60042
+ orders: long_state_trades
60043
+ });
60044
+ }
60045
+ if (trades.short.length !== short_state_trades.length && positions.short.quantity !== short_state.position.quantity) {
60046
+ let _position = {
60047
+ entry: short_state.position.entry,
60048
+ quantity: short_state.position.quantity
60049
+ };
60050
+ let tp = undefined;
60051
+ if (short_state_trades.length === 0 && positions.short.quantity === 0) {
60052
+ _position.entry = 0;
60053
+ _position.quantity = 0;
60054
+ short_state.position.entry = 0;
60055
+ short_state.position.quantity = 0;
60056
+ tp = {
60057
+ price: 0,
60058
+ quantity: 0
60059
+ };
60060
+ }
60061
+ await updateDbDetails(client, {
60062
+ kind: "short",
60063
+ symbol
60064
+ }, raw_data, {
60065
+ position: _position,
60066
+ orders: short_state_trades,
60067
+ take_profit: tp
60068
+ });
60069
+ }
61098
60070
  }
61099
- const long_position = long_state.position.get();
61100
- const short_position = short_state.position.get();
60071
+ const long_position = long_state.position;
60072
+ const short_position = short_state.position;
61101
60073
  const long_quantity = long_position.quantity;
61102
60074
  const short_quantity = short_position.quantity;
61103
60075
  const long = {
@@ -61320,7 +60292,11 @@ async function placeTpOrder3(client, payload) {
61320
60292
  }
61321
60293
  async function cancelAllOrders3(client, symbol, payload) {
61322
60294
  const { type, side, kind } = payload;
61323
- const { orders: fullOrders, symbol_config } = await fetchDBExchangeData(client, symbol);
60295
+ const {
60296
+ orders: fullOrders,
60297
+ symbol_config,
60298
+ positions
60299
+ } = await fetchDBExchangeData(client, symbol);
61324
60300
  const allOrders = await getOpenOrders3(client, symbol, type);
61325
60301
  const sameKindOrders = fullOrders.filter((o) => o.positionSide.toLowerCase() === kind);
61326
60302
  const ordersToCancel = allOrders.filter((x) => side ? x.side === side : true).filter((x) => kind ? x.kind === kind : true);
@@ -61331,9 +60307,13 @@ async function cancelAllOrders3(client, symbol, payload) {
61331
60307
  decimal_places: symbol_config.decimal_places,
61332
60308
  price_places: symbol_config.price_places
61333
60309
  }, {
61334
- position: {},
60310
+ position: positions[kind] || {},
61335
60311
  orders: excludeFromSameKind,
61336
- raw: true
60312
+ raw: true,
60313
+ stop_loss: {
60314
+ price: 0,
60315
+ quantity: 0
60316
+ }
61337
60317
  });
61338
60318
  }
61339
60319
  async function cancelOrdersParallel2(payload) {
@@ -61370,6 +60350,49 @@ async function cancelOrders3(payload) {
61370
60350
  custom_client: payload.custom_client
61371
60351
  });
61372
60352
  }
60353
+ async function createLimitPurchaseOrders2(client, payload) {
60354
+ const { positions, symbol_config, current_price, raw_data } = await fetchDBExchangeData(client, payload.symbol);
60355
+ const kind = payload.orders[0].kind || payload.kind;
60356
+ const params = {
60357
+ position: {
60358
+ kind,
60359
+ entry: positions[kind]?.entry || 0,
60360
+ quantity: positions[kind]?.quantity || 0
60361
+ },
60362
+ global_config: symbol_config
60363
+ };
60364
+ const generator = new TradeEngine(params);
60365
+ const state = generator.initializeEngine({
60366
+ trades: payload.orders,
60367
+ price: current_price
60368
+ });
60369
+ const current_position_state = {
60370
+ entry: state.position.entry,
60371
+ quantity: state.position.quantity
60372
+ };
60373
+ const remaining_orders = state.newTrades;
60374
+ console.log({
60375
+ length: remaining_orders.length,
60376
+ position: current_position_state,
60377
+ last: remaining_orders[remaining_orders.length - 1]
60378
+ });
60379
+ return await updateDbDetails(client, {
60380
+ kind,
60381
+ symbol: payload.symbol
60382
+ }, raw_data, {
60383
+ position: current_position_state,
60384
+ orders: remaining_orders
60385
+ });
60386
+ return await savePaperDetails(client, {
60387
+ kind,
60388
+ symbol: payload.symbol,
60389
+ price_places: payload.price_places,
60390
+ decimal_places: payload.decimal_places
60391
+ }, {
60392
+ position: current_position_state,
60393
+ orders: remaining_orders
60394
+ });
60395
+ }
61373
60396
 
61374
60397
  class PaperBinanceExchange extends BaseExchange {
61375
60398
  constructor(client) {
@@ -61387,36 +60410,7 @@ class PaperBinanceExchange extends BaseExchange {
61387
60410
  return await cancelAllOrders3(this.client, symbol, payload);
61388
60411
  }
61389
60412
  async _createLimitPurchaseOrders(payload) {
61390
- const { positions, symbol_config, current_price, config: config2 } = await fetchDBExchangeData(this.client, payload.symbol);
61391
- const kind = payload.orders[0].kind;
61392
- const params = {
61393
- position: {
61394
- kind,
61395
- entry: positions[kind]?.entry || 0,
61396
- quantity: positions[kind]?.quantity || 0
61397
- },
61398
- global_config: symbol_config
61399
- };
61400
- const generator = primitiveTradeGenerator(params);
61401
- const state = await generator.initializeEngine({
61402
- config: config2[kind],
61403
- trades: payload.orders,
61404
- price: current_price
61405
- });
61406
- const current_position_state = {
61407
- entry: state.position.get().entry,
61408
- quantity: state.position.get().quantity
61409
- };
61410
- const remaining_orders = state.newTrades();
61411
- return await savePaperDetails(this.client, {
61412
- kind,
61413
- symbol: payload.symbol,
61414
- price_places: payload.price_places,
61415
- decimal_places: payload.decimal_places
61416
- }, {
61417
- position: current_position_state,
61418
- orders: remaining_orders
61419
- });
60413
+ return await createLimitPurchaseOrders2(this.client, payload);
61420
60414
  }
61421
60415
  async analyzeCharts(_payload) {}
61422
60416
  async getExchangeInfo(options) {
@@ -61496,9 +60490,10 @@ class PaperBinanceExchange extends BaseExchange {
61496
60490
  symbol_config,
61497
60491
  current_price,
61498
60492
  orders: _orders,
60493
+ trades,
61499
60494
  config: config2
61500
60495
  } = await fetchDBExchangeData(this.client, _payload.symbol);
61501
- const generator = primitiveTradeGenerator({
60496
+ const generator = new TradeEngine({
61502
60497
  position: {
61503
60498
  kind,
61504
60499
  entry: positions[kind]?.entry || 0,
@@ -61506,9 +60501,10 @@ class PaperBinanceExchange extends BaseExchange {
61506
60501
  },
61507
60502
  global_config: symbol_config
61508
60503
  });
61509
- const long_state = await generator.initializeEngine({
60504
+ const long_state = generator.initializeEngine({
61510
60505
  config: config2[kind],
61511
- price: current_price
60506
+ price: current_price,
60507
+ trades: trades.long
61512
60508
  });
61513
60509
  return long_state;
61514
60510
  }
@@ -61534,6 +60530,14 @@ async function forceClosePosition2(client, symbol, options) {
61534
60530
  entry: 0,
61535
60531
  quantity: 0
61536
60532
  },
60533
+ take_profit: {
60534
+ price: 0,
60535
+ quantity: 0
60536
+ },
60537
+ stop_loss: {
60538
+ price: 0,
60539
+ quantity: 0
60540
+ },
61537
60541
  orders: []
61538
60542
  });
61539
60543
  }