Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

program: add get_worst_case_base_asset_amount for spot to reduce cu's in place_spot_order #662

Merged
merged 5 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 24 additions & 43 deletions programs/drift/src/controller/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::u64;
use anchor_lang::prelude::*;
use solana_program::msg;

use crate::controller;
use crate::controller::funding::settle_funding_payment;
use crate::controller::position;
use crate::controller::position::{
Expand All @@ -23,7 +24,8 @@ use crate::controller::spot_position::{
};
use crate::error::DriftResult;
use crate::error::ErrorCode;
use crate::instructions::{OrderParams, PlaceOrderOptions};
use crate::get_struct_values;
use crate::get_then_update_id;
use crate::load_mut;
use crate::math::amm_jit::calculate_amm_jit_liquidity;
use crate::math::auction::calculate_auction_prices;
Expand All @@ -47,9 +49,9 @@ use crate::math::safe_math::SafeMath;
use crate::math::spot_balance::{get_signed_token_amount, get_token_amount};
use crate::math::stats::calculate_new_twap;
use crate::math::{amm, fees, margin::*, orders::*};
use crate::{controller, PostOnlyParam};
use crate::{get_struct_values, ModifyOrderParams};
use crate::{get_then_update_id, ModifyOrderPolicy};
use crate::state::order_params::{
ModifyOrderParams, ModifyOrderPolicy, OrderParams, PlaceOrderOptions, PostOnlyParam,
};

use crate::math::amm::calculate_amm_available_liquidity;
use crate::math::safe_unwrap::SafeUnwrap;
Expand Down Expand Up @@ -181,9 +183,6 @@ pub fn place_perp_order(
let position_index = get_position_index(&user.perp_positions, market_index)
.or_else(|_| add_new_position(&mut user.perp_positions, market_index))?;

let worst_case_base_asset_amount_before =
user.perp_positions[position_index].worst_case_base_asset_amount()?;

// Increment open orders for existing position
let (existing_position_direction, order_base_asset_amount) = {
validate!(
Expand Down Expand Up @@ -279,6 +278,13 @@ pub fn place_perp_order(
Err(err) => return Err(err),
};

let risk_increasing = is_new_order_risk_increasing(
&new_order,
user.perp_positions[position_index].base_asset_amount,
user.perp_positions[position_index].open_bids,
user.perp_positions[position_index].open_asks,
)?;

user.increment_open_orders(new_order.has_auction());
user.orders[new_order_index] = new_order;
user.perp_positions[position_index].open_orders += 1;
Expand All @@ -290,12 +296,6 @@ pub fn place_perp_order(
)?;
}

let worst_case_base_asset_amount_after =
user.perp_positions[position_index].worst_case_base_asset_amount()?;

let risk_increasing = worst_case_base_asset_amount_after.unsigned_abs()
> worst_case_base_asset_amount_before.unsigned_abs();

options.update_risk_increasing(risk_increasing);

// when orders are placed in bulk, only need to check margin on last place
Expand All @@ -309,7 +309,7 @@ pub fn place_perp_order(
)?;
}

if force_reduce_only && risk_increasing {
if force_reduce_only && !user.orders[new_order_index].reduce_only {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so if a market is in reduce only mode, a user's order will fail without explicitly setting reduce only flag?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feel like a method is_reducing() would be nicer?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_reducing on what? above if the force_reduce_only is true, the order param is forced to be true

return Err(ErrorCode::InvalidOrderNotRiskReducing);
}

Expand Down Expand Up @@ -2856,21 +2856,6 @@ pub fn place_spot_order(
let signed_token_amount = get_signed_token_amount(token_amount, &balance_type)?;

let oracle_price_data = *oracle_map.get_price_data(&spot_market.oracle)?;
let strict_oracle_price = StrictOraclePrice::new(
oracle_price_data.price,
spot_market
.historical_oracle_data
.last_oracle_price_twap_5min,
true,
);

let worst_case_simulation_before = user.spot_positions[spot_position_index]
.get_worst_case_token_amount(
spot_market,
&strict_oracle_price,
Some(signed_token_amount),
MarginRequirementType::Initial,
)?;

// Increment open orders for existing position
let (existing_position_direction, order_base_asset_amount) = {
Expand Down Expand Up @@ -2972,6 +2957,13 @@ pub fn place_spot_order(
spot_market.min_order_size,
)?;

let risk_increasing = is_new_order_risk_increasing(
&new_order,
signed_token_amount.cast()?,
user.spot_positions[spot_position_index].open_bids,
user.spot_positions[spot_position_index].open_asks,
)?;

user.increment_open_orders(new_order.has_auction());
user.orders[new_order_index] = new_order;
user.spot_positions[spot_position_index].open_orders += 1;
Expand All @@ -2983,17 +2975,6 @@ pub fn place_spot_order(
)?;
}

let worst_case_simulation_after = user.spot_positions[spot_position_index]
.get_worst_case_token_amount(
spot_market,
&strict_oracle_price,
Some(signed_token_amount),
MarginRequirementType::Initial,
)?;

// Order fails if it's risk increasing and it brings the user collateral below the margin requirement
let risk_increasing = worst_case_simulation_before.risk_increasing(worst_case_simulation_after);

options.update_risk_increasing(risk_increasing);

if options.enforce_margin_check {
Expand All @@ -3008,7 +2989,7 @@ pub fn place_spot_order(

validate_spot_margin_trading(user, spot_market_map, oracle_map)?;

if force_reduce_only && risk_increasing {
if force_reduce_only && !user.orders[new_order_index].reduce_only {
return Err(ErrorCode::InvalidOrderNotRiskReducing);
}

Expand Down Expand Up @@ -4445,7 +4426,7 @@ pub fn trigger_spot_order(
user.spot_positions[position_index].get_signed_token_amount(&spot_market)?;

let worst_case_simulation_before = user.spot_positions[position_index]
.get_worst_case_token_amount(
.get_worst_case_fill_simulation(
&spot_market,
&strict_oracle_price,
Some(signed_token_amount),
Expand Down Expand Up @@ -4519,7 +4500,7 @@ pub fn trigger_spot_order(

let worst_case_simulation_after = user
.get_spot_position(market_index)?
.get_worst_case_token_amount(
.get_worst_case_fill_simulation(
&spot_market,
&strict_oracle_price,
Some(signed_token_amount),
Expand Down
96 changes: 5 additions & 91 deletions programs/drift/src/instructions/user.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anchor_lang::prelude::*;
use anchor_lang::Discriminator;
use anchor_lang::{prelude::*, AnchorDeserialize, AnchorSerialize};
use anchor_spl::token::{Token, TokenAccount};

use crate::controller::orders::{cancel_orders, ModifyOrderId};
Expand Down Expand Up @@ -43,6 +43,9 @@ use crate::state::fulfillment_params::drift::MatchFulfillmentParams;
use crate::state::fulfillment_params::phoenix::PhoenixFulfillmentParams;
use crate::state::fulfillment_params::serum::SerumFulfillmentParams;
use crate::state::oracle::StrictOraclePrice;
use crate::state::order_params::{
ModifyOrderParams, OrderParams, PlaceOrderOptions, PostOnlyParam,
};
use crate::state::perp_market::MarketStatus;
use crate::state::perp_market_map::{get_writable_perp_market_set, MarketSet};
use crate::state::spot_fulfillment_params::SpotFulfillmentParams;
Expand All @@ -53,9 +56,7 @@ use crate::state::spot_market_map::{
};
use crate::state::state::State;
use crate::state::traits::Size;
use crate::state::user::{
MarketType, OrderTriggerCondition, OrderType, ReferrerName, User, UserStats, UserStatus,
};
use crate::state::user::{MarketType, OrderType, ReferrerName, User, UserStats, UserStatus};
use crate::state::user_map::load_user_maps;
use crate::validate;
use crate::validation::user::validate_user_deletion;
Expand Down Expand Up @@ -738,63 +739,6 @@ pub fn handle_transfer_deposit(
Ok(())
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Copy)]
pub struct OrderParams {
pub order_type: OrderType,
pub market_type: MarketType,
pub direction: PositionDirection,
pub user_order_id: u8,
pub base_asset_amount: u64,
pub price: u64,
pub market_index: u16,
pub reduce_only: bool,
pub post_only: PostOnlyParam,
pub immediate_or_cancel: bool,
pub max_ts: Option<i64>,
pub trigger_price: Option<u64>,
pub trigger_condition: OrderTriggerCondition,
pub oracle_price_offset: Option<i32>,
pub auction_duration: Option<u8>,
pub auction_start_price: Option<i64>,
pub auction_end_price: Option<i64>,
}

#[derive(Clone, Copy, BorshSerialize, BorshDeserialize, PartialEq, Debug, Eq)]
pub enum PostOnlyParam {
None,
MustPostOnly, // Tx fails if order can't be post only
TryPostOnly, // Tx succeeds and order not placed if can't be post only
Slide, // Modify price to be post only if can't be post only
}

impl Default for PostOnlyParam {
fn default() -> Self {
PostOnlyParam::None
}
}

pub struct PlaceOrderOptions {
pub try_expire_orders: bool,
pub enforce_margin_check: bool,
pub risk_increasing: bool,
}

impl Default for PlaceOrderOptions {
fn default() -> Self {
Self {
try_expire_orders: true,
enforce_margin_check: true,
risk_increasing: false,
}
}
}

impl PlaceOrderOptions {
pub fn update_risk_increasing(&mut self, risk_increasing: bool) {
self.risk_increasing = self.risk_increasing || risk_increasing;
}
}

#[access_control(
exchange_not_paused(&ctx.accounts.state)
)]
Expand Down Expand Up @@ -978,36 +922,6 @@ pub fn handle_cancel_orders(
Ok(())
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
pub struct ModifyOrderParams {
pub direction: Option<PositionDirection>,
pub base_asset_amount: Option<u64>,
pub price: Option<u64>,
pub reduce_only: Option<bool>,
pub post_only: Option<PostOnlyParam>,
pub immediate_or_cancel: Option<bool>,
pub max_ts: Option<i64>,
pub trigger_price: Option<u64>,
pub trigger_condition: Option<OrderTriggerCondition>,
pub oracle_price_offset: Option<i32>,
pub auction_duration: Option<u8>,
pub auction_start_price: Option<i64>,
pub auction_end_price: Option<i64>,
pub policy: Option<ModifyOrderPolicy>,
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Eq, PartialEq)]
pub enum ModifyOrderPolicy {
TryModify,
MustModify,
}

impl Default for ModifyOrderPolicy {
fn default() -> Self {
Self::TryModify
}
}

#[access_control(
exchange_not_paused(&ctx.accounts.state)
)]
Expand Down
1 change: 1 addition & 0 deletions programs/drift/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use math::{bn, constants::*};
use state::oracle::OracleSource;

use crate::controller::position::PositionDirection;
use crate::state::order_params::{ModifyOrderParams, OrderParams};
use crate::state::perp_market::{ContractTier, MarketStatus};
use crate::state::spot_market::AssetTier;
use crate::state::spot_market::SpotFulfillmentConfigStatus;
Expand Down
2 changes: 1 addition & 1 deletion programs/drift/src/math/margin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ pub fn calculate_margin_requirement_and_total_collateral_and_liability_info(
weighted_token_value: worst_case_weighted_token_value,
..
} = spot_position
.get_worst_case_token_amount(
.get_worst_case_fill_simulation(
&spot_market,
&strict_oracle_price,
Some(signed_token_amount),
Expand Down
44 changes: 32 additions & 12 deletions programs/drift/src/math/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use crate::math::amm::calculate_amm_available_liquidity;
use crate::math::auction::{is_amm_available_liquidity_source, is_auction_complete};
use crate::math::casting::Cast;
use crate::{
load, math, PostOnlyParam, State, BASE_PRECISION_I128, OPEN_ORDER_MARGIN_REQUIREMENT,
PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_U64, PRICE_PRECISION_I128, QUOTE_PRECISION_I128,
SPOT_WEIGHT_PRECISION, SPOT_WEIGHT_PRECISION_I128,
load, math, State, BASE_PRECISION_I128, OPEN_ORDER_MARGIN_REQUIREMENT, PERCENTAGE_PRECISION,
PERCENTAGE_PRECISION_U64, PRICE_PRECISION_I128, QUOTE_PRECISION_I128, SPOT_WEIGHT_PRECISION,
SPOT_WEIGHT_PRECISION_I128,
};

use crate::math::constants::MARGIN_PRECISION_U128;
Expand All @@ -28,6 +28,7 @@ use crate::print_error;
use crate::state::margin_calculation::{MarginCalculation, MarginContext};
use crate::state::oracle::{OraclePriceData, StrictOraclePrice};
use crate::state::oracle_map::OracleMap;
use crate::state::order_params::PostOnlyParam;
use crate::state::perp_market::{PerpMarket, AMM};
use crate::state::perp_market_map::PerpMarketMap;
use crate::state::spot_market::SpotMarket;
Expand Down Expand Up @@ -602,17 +603,36 @@ pub fn is_order_risk_decreasing(
})
}

pub fn is_order_risk_increasing(
order_direction: &PositionDirection,
order_base_asset_amount: u64,
pub fn is_new_order_risk_increasing(
order: &Order,
position_base_asset_amount: i64,
position_bids: i64,
position_asks: i64,
) -> DriftResult<bool> {
is_order_risk_decreasing(
order_direction,
order_base_asset_amount,
position_base_asset_amount,
)
.map(|risk_decreasing| !risk_decreasing)
if order.reduce_only {
return Ok(false);
}

match order.direction {
PositionDirection::Long => {
if position_base_asset_amount >= 0 {
return Ok(true);
}

Ok(position_bids.safe_add(order.base_asset_amount.cast()?)?
> position_base_asset_amount.abs())
}
PositionDirection::Short => {
if position_base_asset_amount <= 0 {
return Ok(true);
}

Ok(position_asks
.safe_sub(order.base_asset_amount.cast()?)?
.abs()
> position_base_asset_amount)
}
}
}

pub fn is_order_position_reducing(
Expand Down
Loading