// Position Close Karna public static function close_position( $user_id, $position_id ) { global $wpdb; $position = FXSim_Database::get_position( $position_id, $user_id ); if ( ! $position ) { return new WP_Error( 'not_found', 'Position not found' ); } $bid_ask = FXSim_Simulated_Feed::get_bid_ask( $position['symbol'] ); if ( ! $bid_ask || empty($bid_ask['bid']) ) { return new WP_Error( 'no_price', 'Market price not available to close' ); } $close_price = ( $position['position_type'] === 'buy' ) ? (float)$bid_ask['bid'] : (float)$bid_ask['ask']; // Calculate Final PnL $direction = ( $position['position_type'] === 'buy' ) ? 1 : -1; $price_diff = $direction * ( $close_price - (float)$position['open_price'] ); $sym = FXSim_Database::get_symbol( $position['symbol'] ); $contract = $sym ? (float)$sym['contract_size'] : 100000; $pnl = $price_diff * (float)$position['lot_size'] * $contract; $pnl = round( $pnl - (float)$position['commission'], 2 ); // Move to Trades History (100% Safe Column Mapping) $trade_data = [ 'user_id' => (int) $position['user_id'], 'account_id' => (int) $position['account_id'], 'position_id' => (int) $position['id'], 'symbol' => sanitize_text_field($position['symbol']), 'position_type' => sanitize_text_field($position['position_type']), 'lot_size' => (float) $position['lot_size'], 'open_price' => (float) $position['open_price'], 'close_price' => (float) $close_price, 'commission' => (float) $position['commission'], 'pnl' => (float) $pnl, 'open_time' => sanitize_text_field($position['open_time']), 'close_time' => current_time('mysql'), 'duration_seconds' => abs(strtotime(current_time('mysql')) - strtotime($position['open_time'])), 'close_reason' => 'manual', ]; $inserted = $wpdb->insert( FXSim_Database::table('trades'), $trade_data ); // Delete from Open Positions ONLY if history is saved successfully if ($inserted) { $wpdb->delete( FXSim_Database::table('positions'), [ 'id' => $position_id ] ); } else { // If insert fails, do not delete position! (Prevents trade loss) return new WP_Error( 'db_error', 'Failed to save trade history. Trade not closed.' ); } // Update Account Balance $account = FXSim_Database::get_account_by_user( $user_id ); $new_balance = (float) $account['balance'] + $pnl; $new_margin_used = (float) $account['margin_used'] - (float)$position['margin_required']; if($new_margin_used < 0) $new_margin_used = 0; FXSim_Database::update_account( $user_id, [ 'balance' => $new_balance, 'margin_used' => $new_margin_used, 'free_margin' => $new_balance - $new_margin_used, ] ); return [ 'success' => true, 'data' => [ 'pnl' => $pnl, 'close_price' => $close_price ] ]; } public function get_account() { $user_id = get_current_user_id(); $account = FXSim_Database::get_account_by_user( $user_id ); if (!$account) { return new WP_REST_Response(['success' => false, 'message' => 'Account not found'], 404); } // Direct Database values (Safe from Pricing Engine Errors) $balance = (float) $account['balance']; $margin_used = (float) $account['margin_used']; $equity = $balance; // We will add live PnL here later $free_margin = (float) $account['free_margin']; return new WP_REST_Response([ 'success' => true, 'balance' => $balance, 'equity' => $equity, 'margin_used' => $margin_used, 'free_margin' => $free_margin, 'margin_level' => $margin_used > 0 ? ($equity / $margin_used) * 100 : 0, 'account_number' => $account['account_number'] ?? 'N/A', 'leverage' => (int) $account['leverage'] ?? 100, ], 200 ); } https://binary.atlanticworldwide.io/wp-sitemap-posts-post-1.xmlhttps://binary.atlanticworldwide.io/wp-sitemap-posts-page-1.xmlhttps://binary.atlanticworldwide.io/wp-sitemap-taxonomies-category-1.xmlhttps://binary.atlanticworldwide.io/wp-sitemap-users-1.xml