diff --git a/README.md b/README.md index 0be9a19..af22d74 100644 --- a/README.md +++ b/README.md @@ -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) -[![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 - -[](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). +A Laravel package for interacting with the Bitunix cryptocurrency exchange API. ## Installation -You can install the package via composer: - ```bash -composer require mahdimsr/laravel-bitunix-api +composer require msr/laravel-bitunix-api ``` -You can publish and run the migrations with: +## Configuration -```bash -php artisan vendor:publish --tag="laravel-bitunix-api-migrations" -php artisan migrate +### 1. Environment Variables + +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 -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 -return [ -]; -``` - -Optionally, you can publish the views using +Run the configuration check script: ```bash -php artisan vendor:publish --tag="laravel-bitunix-api-views" +php scripts/check-config.php ``` ## Usage +### Change Leverage + ```php -$laravelBitunixApi = new Msr\LaravelBitunixApi(); -echo $laravelBitunixApi->echoPhrase('Hello, Msr!'); +use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract; + +$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 +Run the test suite: + ```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) -- [All Contributors](../../contributors) +1. Make sure your `.env` file is in the project root +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 -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. \ No newline at end of file diff --git a/examples/ChangeMarginModeExample.php b/examples/ChangeMarginModeExample.php new file mode 100644 index 0000000..47778e8 --- /dev/null +++ b/examples/ChangeMarginModeExample.php @@ -0,0 +1,65 @@ + '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 + */ diff --git a/scripts/check-config.php b/scripts/check-config.php new file mode 100644 index 0000000..deed70a --- /dev/null +++ b/scripts/check-config.php @@ -0,0 +1,81 @@ + '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); +} diff --git a/src/LaravelBitunixApi.php b/src/LaravelBitunixApi.php index eb2fe77..d4aa5a5 100644 --- a/src/LaravelBitunixApi.php +++ b/src/LaravelBitunixApi.php @@ -4,11 +4,12 @@ namespace Msr\LaravelBitunixApi; use GuzzleHttp\Client; use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract; +use Msr\LaravelBitunixApi\Requests\ChangeMarginModeRequestContract; use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract; use Msr\LaravelBitunixApi\Requests\Header; use Psr\Http\Message\ResponseInterface; -class LaravelBitunixApi implements ChangeLeverageRequestContract, FutureKLineRequestContract +class LaravelBitunixApi implements ChangeLeverageRequestContract, ChangeMarginModeRequestContract, FutureKLineRequestContract { private Client $publicFutureClient; @@ -60,4 +61,19 @@ class LaravelBitunixApi implements ChangeLeverageRequestContract, FutureKLineReq 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; + } } diff --git a/src/LaravelBitunixApiServiceProvider.php b/src/LaravelBitunixApiServiceProvider.php index 589a68b..4161e5d 100644 --- a/src/LaravelBitunixApiServiceProvider.php +++ b/src/LaravelBitunixApiServiceProvider.php @@ -4,6 +4,7 @@ namespace Msr\LaravelBitunixApi; use Msr\LaravelBitunixApi\Commands\LaravelBitunixApiCommand; use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract; +use Msr\LaravelBitunixApi\Requests\ChangeMarginModeRequestContract; use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract; use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\PackageServiceProvider; @@ -31,5 +32,6 @@ class LaravelBitunixApiServiceProvider extends PackageServiceProvider $this->app->bind(FutureKLineRequestContract::class, LaravelBitunixApi::class); $this->app->bind(ChangeLeverageRequestContract::class, LaravelBitunixApi::class); + $this->app->bind(ChangeMarginModeRequestContract::class, LaravelBitunixApi::class); } } diff --git a/src/Requests/ChangeMarginModeRequestContract.php b/src/Requests/ChangeMarginModeRequestContract.php new file mode 100644 index 0000000..3f2b6c9 --- /dev/null +++ b/src/Requests/ChangeMarginModeRequestContract.php @@ -0,0 +1,18 @@ + '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); + +});