/*
* Copyright (C) 2021-2022 Algodex VASP (BVI) Corp.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
const getTakerOrders = require('./getTakerOrders');
const withMakerTxns = require('./maker/withMakerTxns');
const fromBaseUnits = require('../../utils/units/fromBaseUnits');
const compile = require('../compile');
const logger = require('../../logger');
/**
* # 🏃 getMakerTakerTxns
* This method represents the synthesis of two execution types: [Maker]{@tutorial Maker} and [Taker]{@tutorial Taker} and is represented as "Both".
*
* It first fetches TakerTxns using [getTakerOrders]{@link module:order/structure.getTakerOrders} . If there is a remainder, it concats the return of getTakerOrders
* and the return of [withMakerTxns]{@link module:order/structure.withMakerTxns}.
*
* ### Scenarios:
* 1. There are no Taker orders that meet the criteria.
* * The return of [getTakerOrders]{@link module:order/structure.getTakerOrders} is empty. The entire order is [compiled]{@link module:order/compile} as a maker using [withMakerTxns]{@link module:order/structure.withMakerTxns}.
* 2. There are taker orders and no remainder.
* * The return of [getTakerOrders]{@link module:order/structure.getTakerOrders} is not empty, maker leg is skipped.
* 3. There are taker orders with a remainder
* * The return of [getTakerOrders]{@link module:order/structure.getTakerOrders} is not empty, the remainder is [compiled]{@link module:order/compile} as a maker using [withMakerTxns]{@link module:order/structure.withMakerTxns}.
*
* ### When is it used?
* This execution type is mainly used with our frontend. Since getMakerTakerTxns is robust enough to
* handle both order types, it acts as a catch all method for placing orders with a UI.
*
* If you are planning on integrating Algodex's service into your own UI this is a good method to facillate placing of orders.
* The more granular methods are beneficial for algorithmic trading strategies.
*
*
*
*
* @example
* const [AlgodexAPI]{@link AlgodexApi} = require(@algodex/algodex-sdk)
* const api = new [AlgodexAPI]{@link AlgodexApi}(require('../config.json'))
* const order = {
* "client": api.algod,
* "indexer": api.indexer,
* "asset": {
* "id": 15322902,
* "decimals": 6,
* },
* "address": "TJFFNUYWHPPIYDE4DGGYPGHWKGAPJEWP3DGE5THZS3B2M2XIAPQ2WY3X4I",
* "price": 2.22,
* "amount": 1,
* "total": 2,
* "execution": "both",
* "type": "buy",
* "appId": 22045503,
* "version": 6
* }
*
* let res = await getTakerOrders(api, order)
* console.log(res.contract.txns)
* //Outputs an array with items being some combination of the below :
* [withExecuteAssetTxns]{@link module:txns/sell.withExecuteAssetTxns} || [withExecuteAlgoTxns]{@link module:txns/buy.withExecuteAlgoTxns} || [withPlaceAssetTxns]{@link module:txns/sell.withPlaceAssetTxns} || [withPlaceAlgoTxns]{@link module:txns/buy.withPlaceAlgoTxns}
* @param {AlgodexApi} api The Algodex API
* @param {Order} order The Order from the User
* @return {Promise<Array<Order>>} A promise of all compiled and structured Orders
* @memberOf module:order/structure
* @throws ValidationError
* @see [withExecuteAssetTxns]{@link module:txns/sell.withExecuteAssetTxns} || [withExecuteAlgoTxns]{@link module:txns/buy.withExecuteAlgoTxns} || [withPlaceAssetTxns]{@link module:txns/sell.withPlaceAssetTxns} || [withPlaceAlgoTxns]{@link module:txns/buy.withPlaceAlgoTxns}
*
*/
async function getMakerTakerOrders(api, order) {
if (order.execution !== 'both') {
throw new TypeError(`Unsupported execution of ${order.execution}, use both for automated orderbook matching`);
}
/**
* All Orders
*
* Starts with available Taker Transactions
* @type {Array<Order>}
*/
const originalPrice = order.price;
const roundedTakerPrice = order.type === 'buy'? order.price+= 0.0000009999999 : order. price-= 0.0000009999999;
const orders = [...await getTakerOrders(api, {...order, price: roundedTakerPrice, execution: 'taker'})];
logger.debug( `getMakerTakerOrders: Created ${orders.length} Taker Order(s)`);
/**
* Remainder of the current order
* @type {Number}
*/
const remainder = orders.reduce((result, o) => result -= fromBaseUnits(o.contract.amount, order.asset.decimals), order.amount);
const assetPrecision = 1/(10 ** order.asset.decimals);
if (orders.length > 0 && orders[0].type === 'sell') {
if (remainder * order.price < 0.500001) {
return orders; // This is to prevent placing a maker buy order with a total amount less than 0.5 algo
}
}
// Add Maker Order to response if overflow
if (remainder >= assetPrecision) {
orders.push(
await withMakerTxns(api, await compile({
...order,
price: originalPrice,
execution: 'maker',
amount: remainder,
total: remainder * order.price,
appId: await api.getAppId(order),
indexer: api.indexer,
})),
);
logger.debug(`getMakerTakerOrders: Created 1 Maker Order`);
}
logger.debug(`getMakerTakerOrders: Created ${orders.length} Order(s)`);
return orders;
}
module.exports = getMakerTakerOrders;