future-private: add flash close position request

This commit is contained in:
mahdi msr 2025-09-28 23:58:03 +03:30
parent 4d0a9ebe56
commit e86854ca54
6 changed files with 282 additions and 1 deletions

View File

@ -109,6 +109,23 @@ if ($response->getStatusCode() === 200) {
} }
``` ```
### Flash Close Position
```php
use Msr\LaravelBitunixApi\Requests\FlashClosePositionRequestContract;
$api = app(FlashClosePositionRequestContract::class);
$response = $api->flashClosePosition('19848247723672');
if ($response->getStatusCode() === 200) {
$data = json_decode($response->getBody()->getContents(), true);
if ($data['code'] === 0) {
echo "Position flash closed successfully!";
echo "Position ID: " . $data['data']['positionId'];
}
}
```
### Get Future Kline Data ### Get Future Kline Data
```php ```php
@ -133,6 +150,7 @@ if ($response->getStatusCode() === 200) {
### Trading ### Trading
- `placeOrder(...)` - Place a new order with full support for all order types, take profit, stop loss, and position management - `placeOrder(...)` - Place a new order with full support for all order types, take profit, stop loss, and position management
- `flashClosePosition(string $positionId)` - Flash close position by position ID
### Market Data ### Market Data
@ -152,6 +170,7 @@ if ($response->getStatusCode() === 200) {
- **Change Leverage**: 10 req/sec/uid - **Change Leverage**: 10 req/sec/uid
- **Change Margin Mode**: 10 req/sec/uid - **Change Margin Mode**: 10 req/sec/uid
- **Place Order**: 10 req/sec/uid - **Place Order**: 10 req/sec/uid
- **Flash Close Position**: 5 req/sec/uid
## Error Handling ## Error Handling
@ -190,6 +209,7 @@ Run specific tests:
vendor/bin/pest tests/ChangeLeverageTest.php vendor/bin/pest tests/ChangeLeverageTest.php
vendor/bin/pest tests/ChangeMarginModeTest.php vendor/bin/pest tests/ChangeMarginModeTest.php
vendor/bin/pest tests/PlaceOrderTest.php vendor/bin/pest tests/PlaceOrderTest.php
vendor/bin/pest tests/FlashClosePositionTest.php
vendor/bin/pest tests/HeaderTest.php vendor/bin/pest tests/HeaderTest.php
``` ```
@ -200,6 +220,7 @@ See the `examples/` directory for complete usage examples:
- `ChangeLeverageExample.php` - `ChangeLeverageExample.php`
- `ChangeMarginModeExample.php` - `ChangeMarginModeExample.php`
- `PlaceOrderExample.php` - `PlaceOrderExample.php`
- `FlashClosePositionExample.php`
## Troubleshooting ## Troubleshooting

View File

@ -0,0 +1,117 @@
<?php
/**
* Example usage of Flash Close Position functionality
*
* This example demonstrates how to use the LaravelBitunixApi package
* to flash close positions on Bitunix exchange.
*/
require_once __DIR__.'/../vendor/autoload.php';
use Msr\LaravelBitunixApi\Requests\FlashClosePositionRequestContract;
// 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(FlashClosePositionRequestContract::class);
echo "⚡ Flash Close Position Examples\n\n";
// Example 1: Flash close a single position
echo "1. Flash closing position...\n";
$positionId = '19848247723672';
$response = $api->flashClosePosition($positionId);
if ($response->getStatusCode() === 200) {
$data = json_decode($response->getBody()->getContents(), true);
if ($data['code'] === 0) {
echo "✅ Position flash closed successfully!\n";
echo "Position ID: " . $data['data']['positionId'] . "\n";
} else {
echo "❌ API Error: " . $data['msg'] . "\n";
}
} else {
echo "❌ HTTP Error: " . $response->getStatusCode() . "\n";
}
echo "\n";
// Example 2: Flash close multiple positions
echo "2. Flash closing multiple positions...\n";
$positionIds = [
'19848247723672',
'19848247723673',
'19848247723674'
];
foreach ($positionIds as $positionId) {
echo "Closing position: {$positionId}...\n";
$response = $api->flashClosePosition($positionId);
if ($response->getStatusCode() === 200) {
$data = json_decode($response->getBody()->getContents(), true);
if ($data['code'] === 0) {
echo "✅ Position {$positionId} closed successfully!\n";
} else {
echo "❌ Failed to close position {$positionId}: " . $data['msg'] . "\n";
}
} else {
echo "❌ HTTP Error for position {$positionId}: " . $response->getStatusCode() . "\n";
}
}
echo "\n";
// Example 3: Error handling
echo "3. Error handling example...\n";
$invalidPositionId = 'invalid-position-id';
$response = $api->flashClosePosition($invalidPositionId);
if ($response->getStatusCode() === 200) {
$data = json_decode($response->getBody()->getContents(), true);
if ($data['code'] === 0) {
echo "✅ Position closed successfully!\n";
} else {
echo "❌ API Error: " . $data['msg'] . "\n";
echo "This is expected for invalid position ID\n";
}
} else {
echo "❌ HTTP Error: " . $response->getStatusCode() . "\n";
}
} catch (Exception $e) {
echo '❌ Exception: '.$e->getMessage()."\n";
}
/**
* Flash Close Position Features:
*
* - Closes position by position ID
* - Rate limit: 5 req/sec/uid
* - Immediate position closure
* - No additional parameters required
*
* Important Notes:
*
* - Position ID must be valid and exist
* - Position must be open to be closed
* - This is an immediate action (flash close)
* - Use with caution as it closes positions immediately
*
* Environment Variables Required:
*
* BITUNIX_API_KEY=your-api-key
* BITUNIX_API_SECRET=your-api-secret
* BITUNIX_LANGUAGE=en-US
*/

View File

@ -5,12 +5,13 @@ 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\ChangeMarginModeRequestContract;
use Msr\LaravelBitunixApi\Requests\FlashClosePositionRequestContract;
use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract; use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract;
use Msr\LaravelBitunixApi\Requests\Header; use Msr\LaravelBitunixApi\Requests\Header;
use Msr\LaravelBitunixApi\Requests\PlaceOrderRequestContract; use Msr\LaravelBitunixApi\Requests\PlaceOrderRequestContract;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
class LaravelBitunixApi implements ChangeLeverageRequestContract, ChangeMarginModeRequestContract, FutureKLineRequestContract, PlaceOrderRequestContract class LaravelBitunixApi implements ChangeLeverageRequestContract, ChangeMarginModeRequestContract, FlashClosePositionRequestContract, FutureKLineRequestContract, PlaceOrderRequestContract
{ {
private Client $publicFutureClient; private Client $publicFutureClient;
@ -153,4 +154,17 @@ class LaravelBitunixApi implements ChangeLeverageRequestContract, ChangeMarginMo
return $response; return $response;
} }
public function flashClosePosition(string $positionId): ResponseInterface
{
$body = [
'positionId' => $positionId,
];
$response = $this->getPrivateFutureClient([], $body)->post('trade/flash_close_position', [
'json' => $body,
]);
return $response;
}
} }

View File

@ -5,6 +5,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\ChangeMarginModeRequestContract;
use Msr\LaravelBitunixApi\Requests\FlashClosePositionRequestContract;
use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract; use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract;
use Msr\LaravelBitunixApi\Requests\PlaceOrderRequestContract; use Msr\LaravelBitunixApi\Requests\PlaceOrderRequestContract;
use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\Package;
@ -35,5 +36,6 @@ class LaravelBitunixApiServiceProvider extends PackageServiceProvider
$this->app->bind(ChangeLeverageRequestContract::class, LaravelBitunixApi::class); $this->app->bind(ChangeLeverageRequestContract::class, LaravelBitunixApi::class);
$this->app->bind(ChangeMarginModeRequestContract::class, LaravelBitunixApi::class); $this->app->bind(ChangeMarginModeRequestContract::class, LaravelBitunixApi::class);
$this->app->bind(PlaceOrderRequestContract::class, LaravelBitunixApi::class); $this->app->bind(PlaceOrderRequestContract::class, LaravelBitunixApi::class);
$this->app->bind(FlashClosePositionRequestContract::class, LaravelBitunixApi::class);
} }
} }

View File

@ -0,0 +1,16 @@
<?php
namespace Msr\LaravelBitunixApi\Requests;
use Psr\Http\Message\ResponseInterface;
interface FlashClosePositionRequestContract
{
/**
* Flash close position by position ID
*
* @param string $positionId Position ID
* @return ResponseInterface
*/
public function flashClosePosition(string $positionId): ResponseInterface;
}

View File

@ -0,0 +1,111 @@
<?php
use Msr\LaravelBitunixApi\Requests\FlashClosePositionRequestContract;
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 flash close position successfully', function () {
$api = app(FlashClosePositionRequestContract::class);
expect(fn() => $api->flashClosePosition('19848247723672'))
->not->toThrow(Exception::class);
});
it('validates required position ID parameter', function () {
$api = app(FlashClosePositionRequestContract::class);
// Test with valid position ID
expect(fn() => $api->flashClosePosition('19848247723672'))
->not->toThrow(Exception::class)
->and(fn() => $api->flashClosePosition('123456789'))
->not->toThrow(Exception::class);
});
it('can handle different position ID formats', function () {
$api = app(FlashClosePositionRequestContract::class);
$positionIds = [
'19848247723672',
'123456789',
'987654321',
'position-123',
'pos_456'
];
foreach ($positionIds as $positionId) {
expect(fn() => $api->flashClosePosition($positionId))
->not->toThrow(Exception::class);
}
});
it('validates position ID parameter type', function () {
$api = app(FlashClosePositionRequestContract::class);
// Test with string position ID
expect(fn() => $api->flashClosePosition('19848247723672'))
->not->toThrow(Exception::class)
->and(fn() => $api->flashClosePosition('123456789'))
->not->toThrow(Exception::class);
});
it('can handle edge cases for position ID', function () {
$api = app(FlashClosePositionRequestContract::class);
// Test with long position ID
expect(fn() => $api->flashClosePosition('198482477236721234567890'))
->not->toThrow(Exception::class)
->and(fn() => $api->flashClosePosition('123'))
->not->toThrow(Exception::class);
});
it('validates flash close position method exists', function () {
$api = app(FlashClosePositionRequestContract::class);
expect(method_exists($api, 'flashClosePosition'))->toBeTrue();
});
it('can handle multiple flash close position calls', function () {
$api = app(FlashClosePositionRequestContract::class);
$positionIds = ['19848247723672', '19848247723673', '19848247723674'];
foreach ($positionIds as $positionId) {
expect(fn() => $api->flashClosePosition($positionId))
->not->toThrow(Exception::class);
}
});
it('validates flash close position response structure', function () {
$api = app(FlashClosePositionRequestContract::class);
// This test verifies the method can be called without throwing exceptions
// The actual response structure will be validated by the API
expect(fn() => $api->flashClosePosition('19848247723672'))
->not->toThrow(Exception::class);
});
it('can handle special characters in position ID', function () {
$api = app(FlashClosePositionRequestContract::class);
// Test with position ID containing special characters
expect(fn() => $api->flashClosePosition('pos-123_456'))
->not->toThrow(Exception::class)
->and(fn() => $api->flashClosePosition('pos.123.456'))
->not->toThrow(Exception::class);
});
it('validates flash close position with empty string', function () {
$api = app(FlashClosePositionRequestContract::class);
// This should not throw an exception at the method level
// The API will handle validation
expect(fn() => $api->flashClosePosition(''))
->not->toThrow(Exception::class);
});