@@ -1956,7 +1956,7 @@ mod calculate_max_perp_order_size {
19561956 use crate :: state:: perp_market_map:: PerpMarketMap ;
19571957 use crate :: state:: spot_market:: { SpotBalanceType , SpotMarket } ;
19581958 use crate :: state:: spot_market_map:: SpotMarketMap ;
1959- use crate :: state:: user:: { Order , PerpPosition , SpotPosition , User } ;
1959+ use crate :: state:: user:: { MarginMode , Order , PerpPosition , SpotPosition , User , UserStatus } ;
19601960 use crate :: test_utils:: get_pyth_price;
19611961 use crate :: test_utils:: * ;
19621962 use crate :: {
@@ -3160,6 +3160,191 @@ mod calculate_max_perp_order_size {
31603160 assert ! ( total_collateral. unsigned_abs( ) - margin_requirement < 100 * QUOTE_PRECISION ) ;
31613161 }
31623162
3163+ #[ test]
3164+ pub fn sol_perp_hlm_with_imf ( ) {
3165+ let slot = 0_u64 ;
3166+
3167+ let mut oracle_price = get_pyth_price ( 100 , 6 ) ;
3168+ let oracle_price_key =
3169+ Pubkey :: from_str ( "J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix" ) . unwrap ( ) ;
3170+ let pyth_program = crate :: ids:: pyth_program:: id ( ) ;
3171+ create_account_info ! (
3172+ oracle_price,
3173+ & oracle_price_key,
3174+ & pyth_program,
3175+ oracle_account_info
3176+ ) ;
3177+ let mut oracle_map = OracleMap :: load_one ( & oracle_account_info, slot, None ) . unwrap ( ) ;
3178+
3179+ let mut market = PerpMarket {
3180+ amm : AMM {
3181+ base_asset_reserve : 100 * AMM_RESERVE_PRECISION ,
3182+ quote_asset_reserve : 100 * AMM_RESERVE_PRECISION ,
3183+ bid_base_asset_reserve : 100 * AMM_RESERVE_PRECISION ,
3184+ bid_quote_asset_reserve : 100 * AMM_RESERVE_PRECISION ,
3185+ ask_base_asset_reserve : 100 * AMM_RESERVE_PRECISION ,
3186+ ask_quote_asset_reserve : 100 * AMM_RESERVE_PRECISION ,
3187+ sqrt_k : 100 * AMM_RESERVE_PRECISION ,
3188+ peg_multiplier : 100 * PEG_PRECISION ,
3189+ max_slippage_ratio : 50 ,
3190+ max_fill_reserve_fraction : 100 ,
3191+ order_step_size : 1000 ,
3192+ order_tick_size : 1 ,
3193+ oracle : oracle_price_key,
3194+ base_spread : 0 , // 1 basis point
3195+ historical_oracle_data : HistoricalOracleData {
3196+ last_oracle_price : ( 100 * PRICE_PRECISION ) as i64 ,
3197+ last_oracle_price_twap : ( 100 * PRICE_PRECISION ) as i64 ,
3198+ last_oracle_price_twap_5min : ( 100 * PRICE_PRECISION ) as i64 ,
3199+
3200+ ..HistoricalOracleData :: default ( )
3201+ } ,
3202+ ..AMM :: default ( )
3203+ } ,
3204+ margin_ratio_initial : 1000 ,
3205+ margin_ratio_maintenance : 500 ,
3206+ high_leverage_margin_ratio_initial : 100 ,
3207+ high_leverage_margin_ratio_maintenance : 66 ,
3208+ imf_factor : 50 ,
3209+ status : MarketStatus :: Active ,
3210+ ..PerpMarket :: default_test ( )
3211+ } ;
3212+ market. amm . max_base_asset_reserve = u128:: MAX ;
3213+ market. amm . min_base_asset_reserve = 0 ;
3214+
3215+ create_anchor_account_info ! ( market, PerpMarket , market_account_info) ;
3216+ let market_map = PerpMarketMap :: load_one ( & market_account_info, true ) . unwrap ( ) ;
3217+
3218+ let mut usdc_spot_market = SpotMarket {
3219+ market_index : 0 ,
3220+ oracle_source : OracleSource :: QuoteAsset ,
3221+ cumulative_deposit_interest : SPOT_CUMULATIVE_INTEREST_PRECISION ,
3222+ decimals : 6 ,
3223+ initial_asset_weight : SPOT_WEIGHT_PRECISION ,
3224+ maintenance_asset_weight : SPOT_WEIGHT_PRECISION ,
3225+ deposit_balance : 10000 * SPOT_BALANCE_PRECISION ,
3226+ liquidator_fee : 0 ,
3227+ historical_oracle_data : HistoricalOracleData {
3228+ last_oracle_price_twap : PRICE_PRECISION_I64 ,
3229+ last_oracle_price_twap_5min : PRICE_PRECISION_I64 ,
3230+ ..HistoricalOracleData :: default ( )
3231+ } ,
3232+ ..SpotMarket :: default ( )
3233+ } ;
3234+ create_anchor_account_info ! ( usdc_spot_market, SpotMarket , usdc_spot_market_account_info) ;
3235+ let spot_market_account_infos = Vec :: from ( [ & usdc_spot_market_account_info] ) ;
3236+ let spot_market_map =
3237+ SpotMarketMap :: load_multiple ( spot_market_account_infos, true ) . unwrap ( ) ;
3238+
3239+ let mut spot_positions = [ SpotPosition :: default ( ) ; 8 ] ;
3240+ spot_positions[ 0 ] = SpotPosition {
3241+ market_index : 0 ,
3242+ balance_type : SpotBalanceType :: Deposit ,
3243+ scaled_balance : 10000 * SPOT_BALANCE_PRECISION_U64 ,
3244+ ..SpotPosition :: default ( )
3245+ } ;
3246+ let mut user = User {
3247+ orders : [ Order :: default ( ) ; 32 ] ,
3248+ perp_positions : get_positions ( PerpPosition {
3249+ market_index : 0 ,
3250+ ..PerpPosition :: default ( )
3251+ } ) ,
3252+ spot_positions,
3253+ margin_mode : MarginMode :: HighLeverage ,
3254+ ..User :: default ( )
3255+ } ;
3256+
3257+ let max_order_size = calculate_max_perp_order_size (
3258+ & user,
3259+ 0 ,
3260+ 0 ,
3261+ PositionDirection :: Short ,
3262+ & market_map,
3263+ & spot_market_map,
3264+ & mut oracle_map,
3265+ )
3266+ . unwrap ( ) ;
3267+ assert_eq ! ( max_order_size, 4098356557000 ) ; // 4098
3268+
3269+ let mut spot_positions = [ SpotPosition :: default ( ) ; 8 ] ;
3270+ spot_positions[ 0 ] = SpotPosition {
3271+ market_index : 0 ,
3272+ balance_type : SpotBalanceType :: Deposit ,
3273+ scaled_balance : 100 * SPOT_BALANCE_PRECISION_U64 ,
3274+ ..SpotPosition :: default ( )
3275+ } ;
3276+ let mut user = User {
3277+ orders : [ Order :: default ( ) ; 32 ] ,
3278+ perp_positions : get_positions ( PerpPosition {
3279+ market_index : 0 ,
3280+ ..PerpPosition :: default ( )
3281+ } ) ,
3282+ spot_positions,
3283+ margin_mode : MarginMode :: HighLeverage ,
3284+ ..User :: default ( )
3285+ } ;
3286+
3287+ let max_order_size = calculate_max_perp_order_size (
3288+ & user,
3289+ 0 ,
3290+ 0 ,
3291+ PositionDirection :: Short ,
3292+ & market_map,
3293+ & spot_market_map,
3294+ & mut oracle_map,
3295+ )
3296+ . unwrap ( ) ;
3297+ assert_eq ! ( max_order_size, 84737288000 ) ; // 84
3298+
3299+ let mut spot_positions = [ SpotPosition :: default ( ) ; 8 ] ;
3300+ spot_positions[ 0 ] = SpotPosition {
3301+ market_index : 0 ,
3302+ balance_type : SpotBalanceType :: Deposit ,
3303+ scaled_balance : 10 * SPOT_BALANCE_PRECISION_U64 ,
3304+ ..SpotPosition :: default ( )
3305+ } ;
3306+ let mut user = User {
3307+ orders : [ Order :: default ( ) ; 32 ] ,
3308+ perp_positions : get_positions ( PerpPosition {
3309+ market_index : 0 ,
3310+ ..PerpPosition :: default ( )
3311+ } ) ,
3312+ spot_positions,
3313+ margin_mode : MarginMode :: HighLeverage ,
3314+ ..User :: default ( )
3315+ } ;
3316+
3317+ let max_order_size = calculate_max_perp_order_size (
3318+ & user,
3319+ 0 ,
3320+ 0 ,
3321+ PositionDirection :: Short ,
3322+ & market_map,
3323+ & spot_market_map,
3324+ & mut oracle_map,
3325+ )
3326+ . unwrap ( ) ;
3327+ assert_eq ! ( max_order_size, 9605769000 ) ; // 9.6
3328+
3329+ user. perp_positions [ 0 ] . open_orders = 1 ;
3330+ user. perp_positions [ 0 ] . open_asks = -( max_order_size as i64 ) ;
3331+
3332+ let MarginCalculation {
3333+ margin_requirement,
3334+ total_collateral,
3335+ ..
3336+ } = calculate_margin_requirement_and_total_collateral_and_liability_info (
3337+ & user,
3338+ & market_map,
3339+ & spot_market_map,
3340+ & mut oracle_map,
3341+ MarginContext :: standard ( MarginRequirementType :: Initial ) . strict ( true ) ,
3342+ )
3343+ . unwrap ( ) ;
3344+
3345+ assert ! ( total_collateral. unsigned_abs( ) - margin_requirement < QUOTE_PRECISION ) ;
3346+ }
3347+
31633348 #[ test]
31643349 pub fn swift_failure ( ) {
31653350 let clock_slot = 0_u64 ;
@@ -3385,6 +3570,8 @@ mod calculate_max_perp_order_size {
33853570 )
33863571 . unwrap ( ) ;
33873572
3573+ assert_eq ! ( max_order_size, 1600000 ) ;
3574+
33883575 user. perp_positions [ 0 ] . open_orders += 1 ;
33893576 user. perp_positions [ 0 ] . open_bids += max_order_size as i64 ;
33903577
@@ -3401,7 +3588,10 @@ mod calculate_max_perp_order_size {
34013588 )
34023589 . unwrap ( ) ;
34033590
3404- assert ! ( total_collateral. unsigned_abs( ) - margin_requirement < QUOTE_PRECISION ) ;
3591+ assert_eq ! ( total_collateral. unsigned_abs( ) , 2199358529 ) ; // ~$2200
3592+ assert_eq ! ( margin_requirement, 2186678676 ) ;
3593+
3594+ assert ! ( total_collateral. unsigned_abs( ) - margin_requirement < 13 * QUOTE_PRECISION ) ;
34053595 }
34063596}
34073597
0 commit comments