235 lines
6.7 KiB
TypeScript
235 lines
6.7 KiB
TypeScript
import type {
|
|
OrderSubmission,
|
|
StopOrderSetup,
|
|
StopOrdersSubmission,
|
|
} from '@vegaprotocol/wallet';
|
|
import type {
|
|
OrderFormValues,
|
|
StopOrderFormValues,
|
|
} from '../hooks/use-form-values';
|
|
import * as Schema from '@vegaprotocol/types';
|
|
import { removeDecimal, toNanoSeconds } from '@vegaprotocol/utils';
|
|
import { isPersistentOrder } from './time-in-force-persistence';
|
|
import { type MarketFieldsFragment } from '@vegaprotocol/markets';
|
|
|
|
export const mapFormValuesToOrderSubmission = (
|
|
order: OrderFormValues,
|
|
marketId: string,
|
|
decimalPlaces: number,
|
|
positionDecimalPlaces: number
|
|
): OrderSubmission => ({
|
|
marketId: marketId,
|
|
type: order.type,
|
|
side: order.side,
|
|
timeInForce: order.timeInForce,
|
|
price:
|
|
order.type === Schema.OrderType.TYPE_LIMIT && order.price
|
|
? removeDecimal(order.price, decimalPlaces)
|
|
: undefined,
|
|
size: removeDecimal(order.size, positionDecimalPlaces),
|
|
expiresAt:
|
|
order.expiresAt &&
|
|
order.timeInForce === Schema.OrderTimeInForce.TIME_IN_FORCE_GTT
|
|
? toNanoSeconds(order.expiresAt)
|
|
: undefined,
|
|
postOnly:
|
|
order.type === Schema.OrderType.TYPE_MARKET ||
|
|
[
|
|
Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
|
Schema.OrderTimeInForce.TIME_IN_FORCE_IOC,
|
|
].includes(order.timeInForce)
|
|
? false
|
|
: order.postOnly,
|
|
reduceOnly: ![
|
|
Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
|
Schema.OrderTimeInForce.TIME_IN_FORCE_IOC,
|
|
].includes(order.timeInForce)
|
|
? false
|
|
: order.reduceOnly,
|
|
icebergOpts:
|
|
order.type === Schema.OrderType.TYPE_LIMIT &&
|
|
isPersistentOrder(order.timeInForce) &&
|
|
order.iceberg &&
|
|
order.peakSize &&
|
|
order.minimumVisibleSize
|
|
? {
|
|
peakSize: removeDecimal(order.peakSize, positionDecimalPlaces),
|
|
minimumVisibleSize: removeDecimal(
|
|
order.minimumVisibleSize,
|
|
positionDecimalPlaces
|
|
),
|
|
}
|
|
: undefined,
|
|
});
|
|
|
|
const setTrigger = (
|
|
stopOrderSetup: StopOrderSetup,
|
|
triggerType: StopOrderFormValues['triggerPrice'],
|
|
triggerPrice: StopOrderFormValues['triggerPrice'],
|
|
triggerTrailingPercentOffset: StopOrderFormValues['triggerTrailingPercentOffset'],
|
|
decimalPlaces: number
|
|
) => {
|
|
if (triggerType === 'price') {
|
|
stopOrderSetup.price = removeDecimal(triggerPrice ?? '', decimalPlaces);
|
|
} else if (triggerType === 'trailingPercentOffset') {
|
|
stopOrderSetup.trailingPercentOffset = (
|
|
Number(triggerTrailingPercentOffset) / 100
|
|
).toFixed(3);
|
|
}
|
|
};
|
|
|
|
export const mapFormValuesToStopOrdersSubmission = (
|
|
data: StopOrderFormValues,
|
|
marketId: string,
|
|
decimalPlaces: number,
|
|
positionDecimalPlaces: number
|
|
): StopOrdersSubmission => {
|
|
const submission: StopOrdersSubmission = {};
|
|
const stopOrderSetup: StopOrderSetup = {
|
|
orderSubmission: mapFormValuesToOrderSubmission(
|
|
{
|
|
type: data.type,
|
|
side: data.side,
|
|
size: data.size,
|
|
timeInForce: data.timeInForce,
|
|
price: data.price,
|
|
reduceOnly: true,
|
|
},
|
|
marketId,
|
|
decimalPlaces,
|
|
positionDecimalPlaces
|
|
),
|
|
};
|
|
setTrigger(
|
|
stopOrderSetup,
|
|
data.triggerType,
|
|
data.triggerPrice,
|
|
data.triggerTrailingPercentOffset,
|
|
decimalPlaces
|
|
);
|
|
let oppositeStopOrderSetup: StopOrderSetup | undefined = undefined;
|
|
if (data.oco) {
|
|
oppositeStopOrderSetup = {
|
|
orderSubmission: mapFormValuesToOrderSubmission(
|
|
{
|
|
type: data.ocoType,
|
|
side: data.side,
|
|
size: data.ocoSize,
|
|
timeInForce: data.ocoTimeInForce,
|
|
price: data.ocoPrice,
|
|
reduceOnly: true,
|
|
},
|
|
marketId,
|
|
decimalPlaces,
|
|
positionDecimalPlaces
|
|
),
|
|
};
|
|
setTrigger(
|
|
oppositeStopOrderSetup,
|
|
data.ocoTriggerType,
|
|
data.ocoTriggerPrice,
|
|
data.ocoTriggerTrailingPercentOffset,
|
|
decimalPlaces
|
|
);
|
|
}
|
|
|
|
if (data.expire) {
|
|
const expiresAt = data.expiresAt && toNanoSeconds(data.expiresAt);
|
|
stopOrderSetup.expiresAt = expiresAt;
|
|
stopOrderSetup.expiryStrategy = data.expiryStrategy;
|
|
if (oppositeStopOrderSetup) {
|
|
oppositeStopOrderSetup.expiresAt = expiresAt;
|
|
oppositeStopOrderSetup.expiryStrategy = data.expiryStrategy;
|
|
}
|
|
}
|
|
|
|
if (
|
|
data.triggerDirection ===
|
|
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_RISES_ABOVE
|
|
) {
|
|
submission.risesAbove = stopOrderSetup;
|
|
submission.fallsBelow = oppositeStopOrderSetup;
|
|
}
|
|
if (
|
|
data.triggerDirection ===
|
|
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
|
|
) {
|
|
submission.fallsBelow = stopOrderSetup;
|
|
submission.risesAbove = oppositeStopOrderSetup;
|
|
}
|
|
|
|
return submission;
|
|
};
|
|
|
|
export const mapFormValuesToTakeProfitAndStopLoss = (
|
|
formValues: OrderFormValues,
|
|
market: MarketFieldsFragment
|
|
) => {
|
|
const ocoType =
|
|
formValues.type === Schema.OrderType.TYPE_LIMIT
|
|
? Schema.OrderType.TYPE_MARKET
|
|
: Schema.OrderType.TYPE_LIMIT;
|
|
const orderSubmission = mapFormValuesToOrderSubmission(
|
|
formValues,
|
|
market.id,
|
|
market.decimalPlaces,
|
|
market.positionDecimalPlaces
|
|
);
|
|
|
|
const takeProfitStopOrderSubmission =
|
|
formValues.takeProfit &&
|
|
mapFormValuesToStopOrdersSubmission(
|
|
{
|
|
...formValues,
|
|
price: formValues.takeProfit,
|
|
triggerDirection:
|
|
formValues.side === Schema.Side.SIDE_SELL
|
|
? Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
|
|
: Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_RISES_ABOVE,
|
|
triggerType: 'price',
|
|
ocoTriggerType: 'price',
|
|
expire: false,
|
|
ocoType,
|
|
ocoSize: formValues.size,
|
|
ocoTimeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
|
},
|
|
market.id,
|
|
market.decimalPlaces,
|
|
market.positionDecimalPlaces
|
|
);
|
|
|
|
const stopLossStopOrderSubmission =
|
|
formValues.stopLoss &&
|
|
mapFormValuesToStopOrdersSubmission(
|
|
{
|
|
...formValues,
|
|
price: formValues.stopLoss,
|
|
triggerDirection:
|
|
formValues.side === Schema.Side.SIDE_BUY
|
|
? Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
|
|
: Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_RISES_ABOVE,
|
|
triggerType: 'price',
|
|
ocoTriggerType: 'price',
|
|
expire: false,
|
|
ocoType,
|
|
ocoSize: formValues.size,
|
|
ocoTimeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
|
},
|
|
market.id,
|
|
market.decimalPlaces,
|
|
market.positionDecimalPlaces
|
|
);
|
|
const stopOrdersSubmission = [];
|
|
if (takeProfitStopOrderSubmission) {
|
|
stopOrdersSubmission.push(takeProfitStopOrderSubmission);
|
|
}
|
|
if (stopLossStopOrderSubmission) {
|
|
stopOrdersSubmission.push(stopLossStopOrderSubmission);
|
|
}
|
|
const batchMarketInstructions = {
|
|
submissions: [orderSubmission],
|
|
stopOrdersSubmission,
|
|
};
|
|
return batchMarketInstructions;
|
|
};
|