future-private: add change margin mode request

This commit is contained in:
mahdi msr 2025-09-28 16:02:15 +03:30
parent ccebbf8f70
commit d561f69ece
7 changed files with 424 additions and 46 deletions

190
README.md
View File

@ -1,84 +1,184 @@
# composer package for using bitunix api trading # Laravel Bitunix API Package
[![Latest Version on Packagist](https://img.shields.io/packagist/v/mahdimsr/laravel-bitunix-api.svg?style=flat-square)](https://packagist.org/packages/mahdimsr/laravel-bitunix-api) A Laravel package for interacting with the Bitunix cryptocurrency exchange API.
[![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/mahdimsr/laravel-bitunix-api/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/mahdimsr/laravel-bitunix-api/actions?query=workflow%3Arun-tests+branch%3Amain)
[![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/mahdimsr/laravel-bitunix-api/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/mahdimsr/laravel-bitunix-api/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain)
[![Total Downloads](https://img.shields.io/packagist/dt/mahdimsr/laravel-bitunix-api.svg?style=flat-square)](https://packagist.org/packages/mahdimsr/laravel-bitunix-api)
This is where your description should go. Limit it to a paragraph or two. Consider adding a small example.
## Support us
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/laravel-bitunix-api.jpg?t=1" width="419px" />](https://spatie.be/github-ad-click/laravel-bitunix-api)
We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).
## Installation ## Installation
You can install the package via composer:
```bash ```bash
composer require mahdimsr/laravel-bitunix-api composer require msr/laravel-bitunix-api
``` ```
You can publish and run the migrations with: ## Configuration
```bash ### 1. Environment Variables
php artisan vendor:publish --tag="laravel-bitunix-api-migrations"
php artisan migrate Add the following variables to your `.env` file:
```env
BITUNIX_API_KEY=your-api-key-here
BITUNIX_API_SECRET=your-api-secret-here
BITUNIX_LANGUAGE=en-US
``` ```
You can publish the config file with: ### 2. Publish Configuration (Optional)
```bash ```bash
php artisan vendor:publish --tag="laravel-bitunix-api-config" php artisan vendor:publish --tag=bitunix-api-config
``` ```
This is the contents of the published config file: ### 3. Verify Configuration
```php Run the configuration check script:
return [
];
```
Optionally, you can publish the views using
```bash ```bash
php artisan vendor:publish --tag="laravel-bitunix-api-views" php scripts/check-config.php
``` ```
## Usage ## Usage
### Change Leverage
```php ```php
$laravelBitunixApi = new Msr\LaravelBitunixApi(); use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract;
echo $laravelBitunixApi->echoPhrase('Hello, Msr!');
$api = app(ChangeLeverageRequestContract::class);
$response = $api->changeLeverage('BTCUSDT', 'USDT', 12);
if ($response->getStatusCode() === 200) {
$data = json_decode($response->getBody()->getContents(), true);
if ($data['code'] === 0) {
echo "Leverage changed successfully!";
}
}
```
### Change Margin Mode
```php
use Msr\LaravelBitunixApi\Requests\ChangeMarginModeRequestContract;
$api = app(ChangeMarginModeRequestContract::class);
$response = $api->changeMarginMode('BTCUSDT', 'USDT', 'ISOLATION');
if ($response->getStatusCode() === 200) {
$data = json_decode($response->getBody()->getContents(), true);
if ($data['code'] === 0) {
echo "Margin mode changed successfully!";
}
}
```
### Get Future Kline Data
```php
use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract;
$api = app(FutureKLineRequestContract::class);
$response = $api->getFutureKline('BTCUSDT', '1h', 100);
if ($response->getStatusCode() === 200) {
$data = json_decode($response->getBody()->getContents(), true);
// Process kline data
}
```
## API Methods
### Account Management
- `changeLeverage(string $symbol, string $marginCoin, int $leverage)` - Change leverage
- `changeMarginMode(string $symbol, string $marginCoin, string $marginMode)` - Change margin mode
### Market Data
- `getFutureKline(string $symbol, string $interval, int $limit, ?int $startTime, ?int $endTime, string $type)` - Get kline data
## Configuration Options
| Option | Description | Default |
|--------|-------------|---------|
| `future_base_uri` | Bitunix API base URI | `https://fapi.bitunix.com/` |
| `api_key` | Your API key | From `BITUNIX_API_KEY` env var |
| `api_secret` | Your API secret | From `BITUNIX_API_SECRET` env var |
| `language` | API language | From `BITUNIX_LANGUAGE` env var or `en-US` |
## Rate Limits
- **Change Leverage**: 10 req/sec/uid
- **Change Margin Mode**: 10 req/sec/uid
## Error Handling
All methods return a `ResponseInterface` object. Check the response status and parse the JSON response:
```php
$response = $api->changeLeverage('BTCUSDT', 'USDT', 12);
if ($response->getStatusCode() === 200) {
$data = json_decode($response->getBody()->getContents(), true);
if ($data['code'] === 0) {
// Success
echo "Operation successful: " . $data['msg'];
} else {
// API Error
echo "API Error: " . $data['msg'];
}
} else {
// HTTP Error
echo "HTTP Error: " . $response->getStatusCode();
}
``` ```
## Testing ## Testing
Run the test suite:
```bash ```bash
composer test vendor/bin/pest
``` ```
## Changelog Run specific tests:
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. ```bash
vendor/bin/pest tests/ChangeLeverageTest.php
vendor/bin/pest tests/ChangeMarginModeTest.php
vendor/bin/pest tests/HeaderTest.php
```
## Contributing ## Examples
Please see [CONTRIBUTING](CONTRIBUTING.md) for details. See the `examples/` directory for complete usage examples:
## Security Vulnerabilities - `ChangeLeverageExample.php`
- `ChangeMarginModeExample.php`
Please review [our security policy](../../security/policy) on how to report security vulnerabilities. ## Troubleshooting
## Credits ### API credentials not loading from .env
- [mahdi mansouri](https://github.com/mahdimsr) 1. Make sure your `.env` file is in the project root
- [All Contributors](../../contributors) 2. Check that the variable names match exactly (case-sensitive)
3. Restart your application/server after changing .env
4. Clear config cache: `php artisan config:clear`
### Signature generation fails
1. Verify API key and secret are correct
2. Check that the credentials have the necessary permissions
3. Ensure your system time is synchronized
## Security
- Never commit API credentials to version control
- Use environment variables for all sensitive data
- Restrict API key permissions to minimum required
- Regularly rotate API keys
## License ## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information. MIT License. See LICENSE file for details.
## Support
For issues and questions, please create an issue on the GitHub repository.

View File

@ -0,0 +1,65 @@
<?php
/**
* Example usage of Change Margin Mode functionality
*
* This example demonstrates how to use the LaravelBitunixApi package
* to change margin mode for a trading pair on Bitunix exchange.
*/
require_once __DIR__.'/../vendor/autoload.php';
use Msr\LaravelBitunixApi\Requests\ChangeMarginModeRequestContract;
// Example configuration (in real usage, these would be in your .env file)
config([
'bitunix-api.future_base_uri' => 'https://fapi.bitunix.com/',
'bitunix-api.api_key' => 'your-api-key-here',
'bitunix-api.api_secret' => 'your-api-secret-here',
'bitunix-api.language' => 'en-US',
]);
try {
// Get the API instance
$api = app(ChangeMarginModeRequestContract::class);
// Change margin mode for BTCUSDT pair
$symbol = 'BTCUSDT';
$marginCoin = 'USDT';
$marginMode = 'ISOLATION'; // or 'CROSS'
echo "Changing margin mode for {$symbol} to {$marginMode}...\n";
$response = $api->changeMarginMode($symbol, $marginCoin, $marginMode);
// Check response status
if ($response->getStatusCode() === 200) {
$data = json_decode($response->getBody()->getContents(), true);
if ($data['code'] === 0) {
echo "✅ Margin mode changed successfully!\n";
echo 'Symbol: '.$data['data'][0]['symbol']."\n";
echo 'Margin Coin: '.$data['data'][0]['marginCoin']."\n";
echo 'Margin Mode: '.$data['data'][0]['marginMode']."\n";
} else {
echo '❌ API Error: '.$data['msg']."\n";
}
} else {
echo '❌ HTTP Error: '.$response->getStatusCode()."\n";
}
} catch (Exception $e) {
echo '❌ Exception: '.$e->getMessage()."\n";
}
/**
* Available Margin Modes:
* - ISOLATION: Isolated margin mode
* - CROSS: Cross margin mode
*
* Environment Variables Required:
*
* BITUNIX_API_KEY=your-api-key
* BITUNIX_API_SECRET=your-api-secret
* BITUNIX_LANGUAGE=en-US
*/

81
scripts/check-config.php Normal file
View File

@ -0,0 +1,81 @@
<?php
/**
* Configuration Check Script
*
* This script helps you verify that your Bitunix API configuration is working correctly.
*/
require_once __DIR__ . '/../vendor/autoload.php';
use Msr\LaravelBitunixApi\Requests\Header;
echo "🔍 Checking Bitunix API Configuration...\n\n";
// Check if .env file exists
$envFile = __DIR__ . '/../.env';
if (!file_exists($envFile)) {
echo "❌ .env file not found. Please create one based on .env.example\n";
exit(1);
}
// Load environment variables
if (file_exists($envFile)) {
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos($line, '=') !== false && strpos($line, '#') !== 0) {
list($key, $value) = explode('=', $line, 2);
putenv(trim($key) . '=' . trim($value));
}
}
}
// Check environment variables
$apiKey = getenv('BITUNIX_API_KEY');
$apiSecret = getenv('BITUNIX_API_SECRET');
$language = getenv('BITUNIX_LANGUAGE') ?: 'en-US';
echo "📋 Environment Variables:\n";
echo " BITUNIX_API_KEY: " . (empty($apiKey) ? "❌ Not set" : "✅ Set (" . substr($apiKey, 0, 8) . "...)") . "\n";
echo " BITUNIX_API_SECRET: " . (empty($apiSecret) ? "❌ Not set" : "✅ Set (" . substr($apiSecret, 0, 8) . "...)") . "\n";
echo " BITUNIX_LANGUAGE: " . ($language) . "\n\n";
if (empty($apiKey) || empty($apiSecret)) {
echo "❌ API credentials not configured properly.\n";
echo "Please set BITUNIX_API_KEY and BITUNIX_API_SECRET in your .env file.\n";
exit(1);
}
// Test configuration loading
config([
'bitunix-api.future_base_uri' => 'https://fapi.bitunix.com/',
'bitunix-api.api_key' => $apiKey,
'bitunix-api.api_secret' => $apiSecret,
'bitunix-api.language' => $language,
]);
echo "🔧 Configuration Test:\n";
echo " Base URI: " . config('bitunix-api.future_base_uri') . "\n";
echo " API Key: " . substr(config('bitunix-api.api_key'), 0, 8) . "...\n";
echo " API Secret: " . substr(config('bitunix-api.api_secret'), 0, 8) . "...\n";
echo " Language: " . config('bitunix-api.language') . "\n\n";
// Test header generation
try {
echo "🔐 Testing Header Generation:\n";
$headers = Header::generateHeaders([], '{"test":"value"}');
echo " API Key: " . $headers['api-key'] . "\n";
echo " Sign: " . substr($headers['sign'], 0, 16) . "...\n";
echo " Nonce: " . $headers['nonce'] . "\n";
echo " Timestamp: " . $headers['timestamp'] . "\n";
echo " Language: " . $headers['language'] . "\n";
echo " Content-Type: " . $headers['Content-Type'] . "\n\n";
echo "✅ Configuration is working correctly!\n";
echo "You can now use the Bitunix API package in your application.\n";
} catch (Exception $e) {
echo "❌ Error generating headers: " . $e->getMessage() . "\n";
exit(1);
}

View File

@ -4,11 +4,12 @@ namespace Msr\LaravelBitunixApi;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract; use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract;
use Msr\LaravelBitunixApi\Requests\ChangeMarginModeRequestContract;
use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract; use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract;
use Msr\LaravelBitunixApi\Requests\Header; use Msr\LaravelBitunixApi\Requests\Header;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
class LaravelBitunixApi implements ChangeLeverageRequestContract, FutureKLineRequestContract class LaravelBitunixApi implements ChangeLeverageRequestContract, ChangeMarginModeRequestContract, FutureKLineRequestContract
{ {
private Client $publicFutureClient; private Client $publicFutureClient;
@ -60,4 +61,19 @@ class LaravelBitunixApi implements ChangeLeverageRequestContract, FutureKLineReq
return $response; return $response;
} }
public function changeMarginMode(string $symbol, string $marginCoin, string $marginMode): ResponseInterface
{
$body = [
'symbol' => $symbol,
'marginCoin' => $marginCoin,
'marginMode' => $marginMode,
];
$response = $this->getPrivateFutureClient([], $body)->post('account/change_margin_mode', [
'json' => $body,
]);
return $response;
}
} }

View File

@ -4,6 +4,7 @@ namespace Msr\LaravelBitunixApi;
use Msr\LaravelBitunixApi\Commands\LaravelBitunixApiCommand; use Msr\LaravelBitunixApi\Commands\LaravelBitunixApiCommand;
use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract; use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract;
use Msr\LaravelBitunixApi\Requests\ChangeMarginModeRequestContract;
use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract; use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract;
use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\PackageServiceProvider; use Spatie\LaravelPackageTools\PackageServiceProvider;
@ -31,5 +32,6 @@ class LaravelBitunixApiServiceProvider extends PackageServiceProvider
$this->app->bind(FutureKLineRequestContract::class, LaravelBitunixApi::class); $this->app->bind(FutureKLineRequestContract::class, LaravelBitunixApi::class);
$this->app->bind(ChangeLeverageRequestContract::class, LaravelBitunixApi::class); $this->app->bind(ChangeLeverageRequestContract::class, LaravelBitunixApi::class);
$this->app->bind(ChangeMarginModeRequestContract::class, LaravelBitunixApi::class);
} }
} }

View File

@ -0,0 +1,18 @@
<?php
namespace Msr\LaravelBitunixApi\Requests;
use Psr\Http\Message\ResponseInterface;
interface ChangeMarginModeRequestContract
{
/**
* Change margin mode for a trading pair
*
* @param string $symbol Trading pair (e.g., 'BTCUSDT')
* @param string $marginCoin Margin coin (e.g., 'USDT')
* @param string $marginMode Margin mode ('ISOLATION' or 'CROSS')
* @return ResponseInterface
*/
public function changeMarginMode(string $symbol, string $marginCoin, string $marginMode): ResponseInterface;
}

View File

@ -0,0 +1,96 @@
<?php
use Msr\LaravelBitunixApi\Requests\ChangeMarginModeRequestContract;
beforeEach(function () {
config([
'bitunix-api.future_base_uri' => 'https://fapi.bitunix.com/',
'bitunix-api.api_key' => 'test-api-key',
'bitunix-api.api_secret' => 'test-secret-key',
'bitunix-api.language' => 'en-US',
]);
});
it('can change margin mode successfully', function () {
$api = app(ChangeMarginModeRequestContract::class);
expect(fn() => $api->changeMarginMode('BTCUSDT', 'USDT', 'ISOLATION'))
->not->toThrow(Exception::class)
->and(fn() => $api->changeMarginMode('BTCUSDT', 'USDT', 'CROSS'))
->not->toThrow(Exception::class);
});
it('validates required parameters for change margin mode', function () {
$api = app(ChangeMarginModeRequestContract::class);
expect(fn() => $api->changeMarginMode('BTCUSDT', 'USDT', 'ISOLATION'))
->not->toThrow(Exception::class)
->and(fn() => $api->changeMarginMode('ETHUSDT', 'USDT', 'CROSS'))
->not->toThrow(Exception::class);
});
it('handles different margin modes correctly', function () {
$api = app(ChangeMarginModeRequestContract::class);
$marginModes = ['ISOLATION', 'CROSS'];
foreach ($marginModes as $mode) {
expect(fn () => $api->changeMarginMode('BTCUSDT', 'USDT', $mode))
->not->toThrow(Exception::class);
}
});
it('validates margin mode parameter values', function () {
$api = app(ChangeMarginModeRequestContract::class);
expect(fn() => $api->changeMarginMode('BTCUSDT', 'USDT', 'ISOLATION'))
->not->toThrow(Exception::class)
->and(fn() => $api->changeMarginMode('BTCUSDT', 'USDT', 'CROSS'))
->not->toThrow(Exception::class);
});
it('can handle different trading pairs', function () {
$api = app(ChangeMarginModeRequestContract::class);
$tradingPairs = ['BTCUSDT', 'ETHUSDT', 'ADAUSDT'];
foreach ($tradingPairs as $symbol) {
expect(fn () => $api->changeMarginMode($symbol, 'USDT', 'ISOLATION'))
->not->toThrow(Exception::class);
}
});
it('can handle different margin coins', function () {
$api = app(ChangeMarginModeRequestContract::class);
$marginCoins = ['USDT', 'BTC', 'ETH'];
foreach ($marginCoins as $coin) {
expect(fn () => $api->changeMarginMode('BTCUSDT', $coin, 'ISOLATION'))
->not->toThrow(Exception::class);
}
});
it('validates margin mode constants', function () {
$api = app(ChangeMarginModeRequestContract::class);
$validModes = ['ISOLATION', 'CROSS'];
foreach ($validModes as $mode) {
expect(fn () => $api->changeMarginMode('BTCUSDT', 'USDT', $mode))
->not->toThrow(Exception::class);
}
});
it('handles edge cases for margin mode', function () {
$api = app(ChangeMarginModeRequestContract::class);
expect(fn() => $api->changeMarginMode('BTCUSDT', 'USDT', 'ISOLATION'))
->not->toThrow(Exception::class)
->and(fn() => $api->changeMarginMode('ETHUSDT', 'USDT', 'CROSS'))
->not->toThrow(Exception::class);
});