金融取引所システムのシミュレーターです。注文の発注・取消、約定処理、板情報取得、ポジション管理、MarketMaker機能などを提供します。
- 注文管理: 指値注文・成行注文の発注と取消
- 約定処理: リアルタイムでの注文マッチング
- 板情報取得: 買い注文・売り注文の価格・数量情報
- 約定履歴管理: ページネーション付き約定履歴取得・PostgreSQL永続化
- ポジション管理: 取引履歴・損益計算・ポートフォリオ管理
- MarketMaker機能: MARKET_MAKER専用の一括注文機能
- 商品タイプ管理: Cash(現物)とFX(先物)の取引制限
- JWT認証: セキュアなAPIアクセス
- 権限ベースアクセス制御: 役割別API制限
- Java: 17+
- Spring Boot: 3.x
- Spring Security: JWT認証・権限管理
- Spring AOP: 権限チェック
- PostgreSQL: メインデータストレージ(取引履歴、ポジション、ユーザー認証)
- Spring Data JPA: データベースアクセス
- Async Processing: @Async で重い初期化処理を非同期化
- Gradle: ビルドツール
- JUnit 5: テストフレームワーク
- Google BigQuery (オプション): 本番環境での大規模データストレージ(現在無効化)
- 空売り禁止: 保有ポジション以上の売り注文は拒否
- 例: G_BTCJPY, B_BTCJPY, TESTJPY
- 自由取引: 売り・買いどちらからでも取引開始可能
- 例: G_FX_BTCJPY, B_FX_BTCJPY
- 基本的な注文・取引機能
- ポジション確認・取引履歴
- USER権限に加えて
- MarketMake一括注文機能
- 既存注文の一括キャンセル・再投入
POST /api/auth/signup
curl -X POST http://localhost:8080/api/auth/signup \
-H "Content-Type: application/json" \
-d '{
"username": "trader001",
"password": "SecurePass123"
}'Request Fields:
username(string, required): ユーザー名(3-50文字)password(string, required): パスワード(6文字以上)
Response:
{
"message": "User registered successfully",
"username": "trader001"
}Validation Rules:
- ユーザー名は3-50文字の間である必要があります
- パスワードは6文字以上である必要があります
- 既に存在するユーザー名は使用できません
- ユーザーは自動的に "USER" ロールが付与されます
Error Responses:
400 Bad Request: バリデーションエラー500 Internal Server Error: BigQuery接続エラー
POST /api/auth/login
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "trader001",
"password": "SecurePass123"
}'Response:
{
"token": "eyJhbGciOiJIUzI1NiJ9..."
}GET /api/orders/list?symbol=B_FX_BTCJPY&status=NEW
ユーザーの注文一覧を取得します。デフォルトでは NEWステータスのみを返します。
# デフォルト:新規注文(NEW)のみを取得
curl -X GET "http://localhost:8080/api/orders/list" \
-H "Authorization: Bearer <JWT_TOKEN>"
# 特定銘柄のNEW注文を取得
curl -X GET "http://localhost:8080/api/orders/list?symbol=B_FX_BTCJPY" \
-H "Authorization: Bearer <JWT_TOKEN>"
# 複数ステータスを指定(NEW,PARTIALLY_FILLED)
curl -X GET "http://localhost:8080/api/orders/list?status=NEW,PARTIALLY_FILLED" \
-H "Authorization: Bearer <JWT_TOKEN>"
# すべてのステータスを取得
curl -X GET "http://localhost:8080/api/orders/list?status=NEW,PARTIALLY_FILLED,FILLED,CANCELED,REJECTED" \
-H "Authorization: Bearer <JWT_TOKEN>"Query Parameters:
symbol(string, optional): 銘柄フィルタ(例:B_FX_BTCJPY)status(string, optional): ステータスフィルタ。カンマ区切りで複数指定可能NEW- 新規注文(デフォルト)PARTIALLY_FILLED- 部分約定FILLED- 全約定CANCELED- キャンセル済みREJECTED- 却下
Response:
{
"username": "trader001",
"totalOrders": 2,
"orders": [
{
"clOrdID": "order-123",
"symbol": "B_FX_BTCJPY",
"side": "BUY",
"ordType": "LIMIT",
"ordStatus": "NEW",
"orderPx": 5000000.0,
"orderQty": 0.1,
"leavesQty": 0.1,
"filledQty": 0.0,
"tif": "GTC",
"timestamp": "2025-10-06T12:00:00"
},
{
"clOrdID": "order-456",
"symbol": "G_FX_BTCJPY",
"side": "SELL",
"ordType": "LIMIT",
"ordStatus": "PARTIAL_FILL",
"orderPx": 5100000.0,
"orderQty": 0.5,
"leavesQty": 0.3,
"filledQty": 0.2,
"tif": "GTC",
"timestamp": "2025-10-06T11:30:00"
}
]
}Response Fields:
username(string): ユーザー名totalOrders(number): 取得した注文総数orders(array): 注文詳細のリストclOrdID(string): 注文ID(キャンセル時に使用)symbol(string): 銘柄名side(string): 売買区分(BUY/SELL)ordType(string): 注文タイプ(LIMIT/MARKET)ordStatus(string): 注文ステータス(NEW/PARTIALLY_FILLED/FILLED/CANCELED/REJECTED)orderPx(number): 注文価格orderQty(number): 注文数量leavesQty(number): 未約定数量filledQty(number): 約定済み数量tif(string): 注文有効期限(GTC/IOC/FOK)timestamp(string): 注文作成日時openClose(string, optional): 建玉区分(OPEN/CLOSE)。オプショナルフィールドで、nullの場合もあるprofitLoss(number, optional): FIFO方式の損益(CLOSE注文のみ、OPEN注文はnull)
特徴:
- ✅ ステータスフィルタ:
statusパラメータで返すステータスを制御(デフォルト:NEW) - ✅ 複数ステータス指定: カンマ区切りで複数ステータスを指定可能
- ✅ リアルタイム: MarketBoardから直接取得
- ✅ 時系列ソート: 新しい注文から降順で表示
- ✅ 約定状況表示: 注文数量、未約定数量、約定済み数量を確認可能
- ✅ JWT認証必須: ユーザー自身の注文のみ取得
GET /api/orders/{clOrdId}/status
指定した注文ID(clOrdId)のステータスと約定数量・約定価格を取得します。板に載っている注文は NEW / PARTIALLY_FILLED、約定済み・キャンセル済みは executions から FILLED / CANCELLED / EXPIRED を返します。
# 存在する注文のステータス取得
curl -X GET "http://localhost:8080/api/orders/your-cl-ord-id/status" \
-H "Authorization: Bearer <JWT_TOKEN>"Path Parameters:
clOrdId(string, required): 注文ID(発注時に返却された clOrdID、または注文一覧の clOrdID)
Response (200 OK):
{
"clOrdId": "abc-123",
"ordStatus": "FILLED",
"filledQty": 0.001,
"filledPrice": 10700837.0
}Response Fields:
clOrdId(string): 注文IDordStatus(string): 注文ステータス(NEW / PARTIALLY_FILLED / FILLED / CANCELLED / EXPIRED)filledQty(number): 約定済み数量の合計(表示用)filledPrice(number): 約定価格(複数約定時は数量加重平均 VWAP、未約定時は 0)
Error (404 Not Found): 注文が存在しない場合(板にも executions にも無い)
{
"error": "Order not found: <clOrdId>"
}注意: 本APIは Rust 版で実装されています。Java 版では利用できません。
POST /api/orders/new
curl -X POST http://localhost:8080/api/orders/new \
-H "Authorization: Bearer <JWT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"symbol": "G_FX_BTCJPY",
"price": 5000000,
"quantity": 1,
"side": "BUY",
"ordType": "LIMIT",
"tif": "GTC"
}'Request Fields:
symbol(string, required): 取引ペアprice(number, required): 注文価格quantity(number, required): 注文数量side(string, required): 売買区分(BUY/SELL)ordType(string, required): 注文タイプ(LIMIT/MARKET)tif(string, required): 注文有効期限(GTC/IOC/FOK)openClose(string, optional): 建玉区分(OPEN/CLOSE)。オプショナルフィールド
Cash商品の制限:
- 売り注文時は保有ポジションをチェック
- 不足時は
Insufficient position for cash saleエラー
POST /api/orders/cancel
curl -X POST http://localhost:8080/api/orders/cancel \
-H "Authorization: Bearer <JWT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"clOrdID": "order-uuid-string",
"symbol": "G_FX_BTCJPY"
}'GET /api/executions/history?page=0&size=20&symbol=B_FX_BTCJPY
# 全銘柄の約定履歴(最新20件、FILLED/PARTIAL_FILLのみ)
curl -X GET "http://localhost:8080/api/executions/history?page=0&size=20" \
-H "Authorization: Bearer <JWT_TOKEN>"
# 特定銘柄の約定履歴(最新10件)
curl -X GET "http://localhost:8080/api/executions/history?page=0&size=10&symbol=B_FX_BTCJPY" \
-H "Authorization: Bearer <JWT_TOKEN>"
# 2ページ目(21-40件目)の約定履歴
curl -X GET "http://localhost:8080/api/executions/history?page=1&size=20" \
-H "Authorization: Bearer <JWT_TOKEN>"Query Parameters:
page(int, optional): ページ番号(0から開始)、デフォルト: 0size(int, optional): 1ページあたりの件数、デフォルト: 20symbol(string, optional): 銘柄フィルタ
特徴:
- ✅ ページネーション対応: 大量の約定履歴を効率的に取得
- ✅ 約定のみ表示:
FILLEDとPARTIAL_FILLのみ(NEWは除外) - ✅ 永続化: PostgreSQLデータベースに保存された履歴データ
- ✅ 時系列ソート: 最新の約定から降順で表示
- ✅ 約定時刻付き:
createdAtフィールドで正確な約定時刻を取得可能
Response Fields(executions内の各約定):
execID(string): 約定IDclOrdID(string): 注文IDsymbol(string): 銘柄名execStatus(string): 約定ステータス(FILLED/PARTIAL_FILL)lastPx(number): 約定価格lastQty(number): 約定数量counterPartyUsername(string): 相手方ユーザー名side(string): 売買区分(BUY/SELL)createdAt(string): 約定時刻(ISO 8601形式、UTC時刻)
GET /api/executions/all?page=0&size=20&symbol=B_FX_BTCJPY
# 全ユーザーの約定履歴(最新20件、FILLED/PARTIAL_FILLのみ)
curl -X GET "http://localhost:8080/api/executions/all?page=0&size=20" \
-H "Authorization: Bearer <JWT_TOKEN>"
# 特定銘柄の全ユーザー約定履歴(最新10件)
curl -X GET "http://localhost:8080/api/executions/all?page=0&size=10&symbol=B_FX_BTCJPY" \
-H "Authorization: Bearer <JWT_TOKEN>"
# 2ページ目(21-40件目)の全ユーザー約定履歴
curl -X GET "http://localhost:8080/api/executions/all?page=1&size=20" \
-H "Authorization: Bearer <JWT_TOKEN>"Query Parameters:
page(int, optional): ページ番号(0から開始)、デフォルト: 0size(int, optional): 1ページあたりの件数、デフォルト: 20symbol(string, optional): 銘柄フィルタ
特徴:
- ✅ 全ユーザー対象: システム全体の約定履歴を取得
- ✅ ページネーション対応: 大量の約定履歴を効率的に取得
- ✅ 約定のみ表示:
FILLEDとPARTIAL_FILLのみ(NEWは除外) - ✅ 永続化: PostgreSQLデータベースに保存された履歴データ
- ✅ 時系列ソート: 最新の約定から降順で表示
GET /api/executions/volume?symbol=B_FX_BTCJPY&fromTime=2025-06-30T10:00:00&toTime=2025-06-30T12:00:00
# 特定銘柄の約定量計算(2時間分)
curl -X GET "http://localhost:8080/api/executions/volume?symbol=B_FX_BTCJPY&fromTime=2025-06-30T10:00:00&toTime=2025-06-30T12:00:00" \
-H "Authorization: Bearer <JWT_TOKEN>"
# 全銘柄の約定量計算(1日分)
curl -X GET "http://localhost:8080/api/executions/volume?symbol=ALL&fromTime=2025-06-30T00:00:00&toTime=2025-06-30T23:59:59" \
-H "Authorization: Bearer <JWT_TOKEN>"
# 今日の約定量計算(特定銘柄)
curl -X GET "http://localhost:8080/api/executions/volume?symbol=G_FX_BTCJPY&fromTime=2025-06-30T00:00:00&toTime=2025-06-30T23:59:59" \
-H "Authorization: Bearer <JWT_TOKEN>"Query Parameters:
symbol(string, required): 銘柄名("ALL"で全銘柄対象)fromTime(string, required): 開始時刻(yyyy-MM-ddTHH:mm:ss形式、UTC時刻)toTime(string, required): 終了時刻(yyyy-MM-ddTHH:mm:ss形式、UTC時刻)
特徴:
- ✅ 時間範囲指定: 任意の期間での約定量集計
- ✅ 銘柄フィルタ: 特定銘柄または全銘柄対応
- ✅ 約定のみ対象:
FILLEDとPARTIAL_FILLのみ(NEWは除外) - ✅ MarketMaker除外: 一般ユーザーの取引のみ集計
- ✅ 統計情報: 総約定量と約定回数を提供
- ✅ UTC時刻: 全ての時刻はUTC基準で処理
Response:
{
"symbol": "B_FX_BTCJPY",
"fromTime": "2025-06-30T10:00:00",
"toTime": "2025-06-30T12:00:00",
"totalVolume": 1.5,
"executionCount": 12,
"timeRangeDescription": "From 2025-06-30 10:00:00 to 2025-06-30 12:00:00"
}Response:
{
"username": "testuser",
"page": 0,
"size": 5,
"totalPages": 2,
"totalElements": 8,
"executions": [
{
"execID": "23fc010f-9b72-47e7-9505-c65fc61a3826",
"clOrdID": "e5f41070-82f3-4daa-9333-5c3944481ce5",
"symbol": "B_FX_BTCJPY",
"execStatus": "FILLED",
"lastPx": 157641.77,
"lastQty": 0.01,
"counterPartyUsername": "marketmaker1",
"side": "SELL",
"createdAt": "2025-06-25T22:46:48.540225"
}
]
}GET /api/market/board/{symbol}?depth=10
curl -X GET "http://localhost:8080/api/market/board/G_FX_BTCJPY?depth=10"Query Parameters:
symbol(string, required): 銘柄名depth(int, optional): 取得する深さ(デフォルト: 10、最大: 100)
Response:
{
"symbol": "G_FX_BTCJPY",
"bids": [
{
"price": 4999000.0,
"quantity": 1.5
},
{
"price": 4998000.0,
"quantity": 2.0
}
],
"asks": [
{
"price": 5001000.0,
"quantity": 1.0
},
{
"price": 5002000.0,
"quantity": 3.0
}
],
"asOf": 1729329360000000000
}Response Fields:
symbol(string): 銘柄名bids(array): 買い注文(価格降順)price(number): 買い値段quantity(number): 買い数量
asks(array): 売り注文(価格昇順)price(number): 売り値段quantity(number): 売り数量
asOf(long): 最後に注文が入ったときの時刻(エポックからのナノ秒)System.currentTimeNano()で取得したエポック時刻- 例:
1729329360000000000= 2024-10-19T01:16:00Z
特徴:
- ✅ 深さ指定: 1~100レベルの板情報を取得可能
- ✅ リアルタイム: マーケットボードから直接取得
- ✅ asOfタイムスタンプ: 最後の注文入力時刻をナノ秒精度で提供
GET /api/market/board/{symbol}/simple
curl -X GET "http://localhost:8080/api/market/board/G_FX_BTCJPY/simple"特徴:
- ✅ デフォルト深さ5: 深さパラメータなしで深さ5の板情報を取得(asOf含む)
GET /api/positions
全銘柄のポジション情報を一覧で取得します。
curl -X GET "http://localhost:8080/api/positions" \
-H "Authorization: Bearer <JWT_TOKEN>"Response:
[
{
"username": "trader001",
"symbol": "G_FX_BTCJPY",
"unit": "BTC",
"totalBuyQty": 10.0,
"totalBuyAmount": 49500000.0,
"totalSellQty": 5.0,
"totalSellAmount": 25000000.0,
"netQty": 5.0,
"averageBuyPrice": 4950000.0,
"averageSellPrice": 5000000.0,
"realizedPnL": 500.0,
"unrealizedPnL": -200.0,
"totalPnL": 300.0,
"lastUpdated": "2025-06-30T12:00:00"
},
{
"username": "trader001",
"symbol": "B_FX_BTCJPY",
"unit": "BTC",
"totalBuyQty": 5.0,
"totalBuyAmount": 77600000.0,
"totalSellQty": 2.5,
"totalSellAmount": 38900000.0,
"netQty": 2.5,
"averageBuyPrice": 15520000.0,
"averageSellPrice": 15560000.0,
"realizedPnL": 1000.0,
"unrealizedPnL": 150.0,
"totalPnL": 1150.0,
"lastUpdated": "2025-06-30T12:00:00"
}
]Response Fields:
username(string): ユーザー名symbol(string): 銘柄名unit(string): 数量の単位(例: BTC)totalBuyQty(number): 累計買い数量(ポジションがフラットまたは反転時にリセット)totalBuyAmount(number): 累計買い金額(ポジションがフラットまたは反転時にリセット)totalSellQty(number): 累計売り数量(ポジションがフラットまたは反転時にリセット)totalSellAmount(number): 累計売り金額(ポジションがフラットまたは反転時にリセット)netQty(number): ネットポジション数量(買い-売り)averageBuyPrice(number): 平均買い単価(現在のポジションの平均取得価格)averageSellPrice(number): 平均売り単価(現在のポジションの平均売却価格)realizedPnL(number): 実現損益(確定済みの損益)unrealizedPnL(number): 未実現損益(現在価格での含み損益)totalPnL(number): 合計損益(実現+未実現)lastUpdated(string): 最終更新日時
損益計算の仕様:
- ポジションがフラット(netQty = 0)になると、累積値と平均価格はリセットされます
- ポジションが反転(ロング→ショート、ショート→ロング)すると、反対側の累積値がリセットされ、新しいポジションとして管理されます
- これにより、実現損益が実際の売買価格と正確に一致します
GET /api/positions/summary
全ポジションのサマリー情報を取得します。
curl -X GET "http://localhost:8080/api/positions/summary" \
-H "Authorization: Bearer <JWT_TOKEN>"Response:
{
"username": "trader001",
"cashBalance": 1000000.0,
"totalValue": 1001300.0,
"totalRealizedPnL": 1500.0,
"totalUnrealizedPnL": -200.0,
"totalPnL": 1300.0,
"totalTradeCount": 15,
"totalTradingVolume": 50000000.0,
"positions": [
{
"username": "trader001",
"symbol": "G_FX_BTCJPY",
"unit": "BTC",
"totalBuyQty": 10.0,
"totalBuyAmount": 49500000.0,
"totalSellQty": 5.0,
"totalSellAmount": 25000000.0,
"netQty": 5.0,
"averageBuyPrice": 4950000.0,
"averageSellPrice": 5000000.0,
"realizedPnL": 500.0,
"unrealizedPnL": -200.0,
"totalPnL": 300.0,
"lastUpdated": "2025-06-30T12:00:00"
}
],
"symbolTradeCounts": {
"G_FX_BTCJPY": 10
}
}Response Fields:
username(string): ユーザー名cashBalance(number): 現金残高totalValue(number): 総資産(現金 + ポジション評価額)totalRealizedPnL(number): 全銘柄の実現損益合計totalUnrealizedPnL(number): 全銘柄の未実現損益合計totalPnL(number): 全銘柄の合計損益totalTradeCount(number): 総取引回数totalTradingVolume(number): 総取引金額positions(array): 各銘柄のポジション詳細(PositionResponseオブジェクト)symbolTradeCounts(object): 銘柄別取引回数
GET /api/positions/{symbol}
特定銘柄のポジション情報を取得します。
curl -X GET "http://localhost:8080/api/positions/G_FX_BTCJPY" \
-H "Authorization: Bearer <JWT_TOKEN>"Response:
{
"username": "trader001",
"symbol": "G_FX_BTCJPY",
"unit": "BTC",
"totalBuyQty": 10.0,
"totalBuyAmount": 49500000.0,
"totalSellQty": 5.0,
"totalSellAmount": 25000000.0,
"netQty": 5.0,
"averageBuyPrice": 4950000.0,
"averageSellPrice": 5000000.0,
"realizedPnL": 500.0,
"unrealizedPnL": -200.0,
"totalPnL": 300.0,
"lastUpdated": "2025-06-30T12:00:00"
}GET /api/positions/trades?limit=50&symbol=G_FX_BTCJPY
取引履歴を取得します。
# 全銘柄の最新20件
curl -X GET "http://localhost:8080/api/positions/trades?limit=20" \
-H "Authorization: Bearer <JWT_TOKEN>"
# 特定銘柄の取引履歴
curl -X GET "http://localhost:8080/api/positions/trades?limit=20&symbol=G_FX_BTCJPY" \
-H "Authorization: Bearer <JWT_TOKEN>"Query Parameters:
limit(int, optional): 取得件数(デフォルト: 50)symbol(string, optional): 銘柄フィルタ
POST /api/trade/insert
板情報をチェックしてマッチする注文があれば注文を発注・約定させ、ない場合は約定を直接挿入します。
# BUY注文の場合:ASK側の板をチェックしてマッチング
curl -X POST http://localhost:8080/api/trade/insert \
-H "Authorization: Bearer <JWT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"symbol": "B_FX_BTCJPY",
"price": 15525000,
"quantity": 0.02,
"side": "BUY"
}'
# SELL注文の場合:BID側の板をチェックしてマッチング
curl -X POST http://localhost:8080/api/trade/insert \
-H "Authorization: Bearer <JWT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"symbol": "B_FX_BTCJPY",
"price": 15520000,
"quantity": 0.01,
"side": "SELL"
}'Request Fields:
symbol(string, required): 取引ペアprice(number, required): 取引価格quantity(number, required): 取引数量side(string, required): 売買区分(BUY/SELL)
動作ロジック:
- BUY注文の場合: ASK側の板をチェックし、指定価格以下のASK注文があれば自動マッチング
- SELL注文の場合: BID側の板をチェックし、指定価格以上のBID注文があれば自動マッチング
- マッチする注文がある場合: IOC(Immediate or Cancel)注文を発注して自然にマッチング
- マッチする注文がない場合: 約定を直接データベースに挿入
Response(マッチング成功時):
{
"type": "ORDER_PLACED",
"symbol": "B_FX_BTCJPY",
"side": "BUY",
"price": 15525000.0,
"quantity": 0.02,
"message": "Order placed and matched against existing orders",
"executions": [
{
"execID": "abc-123-def",
"execStatus": "FILLED",
"lastPx": 15524893.0,
"lastQty": 0.02
}
]
}Response(直接挿入時):
{
"type": "EXECUTION_INSERTED",
"symbol": "B_FX_BTCJPY",
"side": "BUY",
"price": 15525000.0,
"quantity": 0.02,
"message": "Execution inserted directly (no matching orders found)",
"executions": [
{
"execID": "xyz-456-ghi",
"execStatus": "FILLED",
"lastPx": 15525000.0,
"lastQty": 0.02
}
]
}特徴:
- ✅ 自動マッチング: 既存の板注文と価格が合えば自動的に注文マッチング
- ✅ 直接挿入: マッチしない場合は約定を直接作成・永続化
- ✅ 板情報更新: マッチング時は実際の板数量が正しく更新される
- ✅ IOC注文: マッチング時はIOC(即座に約定または取消)で処理
- ✅ 認証必須: JWT認証が必要
POST /api/market-make/orders
curl -X POST http://localhost:8080/api/market-make/orders \
-H "Authorization: Bearer <MARKET_MAKER_JWT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"symbol": "G_FX_BTCJPY",
"bidLevels": [
{"price": 4995000, "quantity": 1},
{"price": 4990000, "quantity": 2},
{"price": 4985000, "quantity": 3}
],
"askLevels": [
{"price": 5005000, "quantity": 1},
{"price": 5010000, "quantity": 2},
{"price": 5015000, "quantity": 3}
]
}'Response:
{
"username": "marketmaker1",
"symbol": "G_FX_BTCJPY",
"cancelledOrdersCount": 6,
"newBidOrdersCount": 3,
"newAskOrdersCount": 3,
"bidOrderIds": ["bid-order-1", "bid-order-2", "bid-order-3"],
"askOrderIds": ["ask-order-1", "ask-order-2", "ask-order-3"],
"status": "SUCCESS",
"message": "Market make orders processed successfully"
}DELETE /api/market-make/orders/{symbol}
curl -X DELETE http://localhost:8080/api/market-make/orders/G_FX_BTCJPY \
-H "Authorization: Bearer <MARKET_MAKER_JWT_TOKEN>"GET /api/market-make/orders/{symbol}/status
curl -X GET http://localhost:8080/api/market-make/orders/G_FX_BTCJPY/status \
-H "Authorization: Bearer <MARKET_MAKER_JWT_TOKEN>"| 商品名 | タイプ | 説明 | 価格精度 | 数量精度 |
|---|---|---|---|---|
| G_BTCJPY | Cash | 現物ビットコイン | 1 | 1000 |
| G_FX_BTCJPY | FX | ビットコイン先物 | 1 | 1000 |
| B_BTCJPY | Cash | 現物ビットコイン | 1 | 1000 |
| B_FX_BTCJPY | FX | ビットコイン先物 | 1 | 1000 |
| TESTJPY | Cash | テスト用現物 | 1 | 1000 |
ユーザー情報はPostgreSQLデータベースに保存されます:
ユーザーテーブル: users (PostgreSQL)
アプリケーション起動時に以下のデフォルトユーザーが自動作成されます:
| ユーザー名 | パスワード | ロール |
|---|---|---|
| admin | admin123 | ROLE_ADMIN, ROLE_USER |
| trader001 | trader123 | ROLE_USER |
| marketmaker1 | mm123 | ROLE_MARKET_MAKER, ROLE_USER |
- 自動計算: 約定時にポジション・損益を自動更新
- 実現損益: 売買確定時の損益
- 未実現損益: 現在価格での含み損益
- 取引履歴: 全約定の詳細記録
- 正確な損益計算: ポジションがフラットまたは反転時に平均価格を自動リセット
- フラットポジション後の新規トレードは、過去の平均価格の影響を受けない
- ポジション反転時(ロング→ショート、ショート→ロング)は新しいポジションとして管理
- 実現損益が実際の売買価格と正確に一致
- FIFO(先入先出)方式: Close注文の損益をFIFO方式で正確に計算
- 自動OPEN/CLOSE判定: ポジション状態から自動的にOPEN/CLOSE を判定
- BUY注文:
- ショートポジションあり(netQty < 0)→ CLOSE(ショート決済)
- それ以外 → OPEN(ロング建玉)
- SELL注文:
- ロングポジションあり(netQty > 0)→ CLOSE(ロング決済)
- それ以外 → OPEN(ショート建玉)
- BUY注文:
- 部分約定対応: 複数回の約定をまたいで累積損益を計算
- 損益計算式:
- ロング決済: (売値 - 買値) × 数量
- ショート決済: (売値 - 買値) × 数量
- データ永続化: TradeHistoryテーブルに以下の情報を保存
open_close: OPEN/CLOSE区分profit_loss: FIFO方式の損益matched_open_exec_ids: マッチしたOpen約定IDのJSON配列
- API統合:
/api/orders/listで CLOSE注文の累積損益をリアルタイム表示 - openCloseフィールド: 注文作成時にオプションで指定可能(nullの場合は自動判定)
使用例:
# OPEN注文を発注(オプション)
curl -X POST http://localhost:8080/api/orders/new \
-H "Authorization: Bearer <JWT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"symbol": "G_FX_BTCJPY",
"price": 5000000,
"quantity": 1,
"side": "BUY",
"ordType": "LIMIT",
"tif": "GTC",
"openClose": "OPEN"
}'
# CLOSE注文を発注して損益を確定(オプション)
curl -X POST http://localhost:8080/api/orders/new \
-H "Authorization: Bearer <JWT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"symbol": "G_FX_BTCJPY",
"price": 5100000,
"quantity": 1,
"side": "SELL",
"ordType": "LIMIT",
"tif": "GTC",
"openClose": "CLOSE"
}'
# 注文リストで損益を確認
curl -X GET "http://localhost:8080/api/orders/list?status=FILLED" \
-H "Authorization: Bearer <JWT_TOKEN>"
# Response: CLOSE注文には profitLoss フィールドが含まれる- アトミック処理: 既存注文キャンセル→新規注文を不可分で実行
- シングルスレッド: 銘柄別ロックでMarketBoardの整合性保証
- 一括管理: 同一ユーザー・銘柄の注文を効率的に管理
- Cash: 保有ポジション以上の売り注文を自動拒否
- FX: 制限なし、自由な売買が可能
{
"error": "MARKET_MAKER role required"
}{
"error": "Insufficient position for cash sale. Available: 0, Requested: 10"
}{
"error": "Invalid symbol: INVALID_SYMBOL"
}NEW: 新規注文(板に追加済み)PARTIAL_FILL: 部分約定FILLED: 全量約定CANCELED: 取消済みREJECTED: 拒否
- Java 17以上
- Gradle 8.x以上
./gradlew build./gradlew test./gradlew bootRunアプリケーションは http://localhost:8080 で起動します。
アプリケーションはFlywayを使用してデータベーススキーマを自動管理します。マイグレーションファイルは src/main/resources/db/migration/ にあります。
マイグレーション履歴:
V1__convert_to_utc.sql- UTC時刻への変換V2__optimize_volume_calculation.sql- 約定量計算の最適化インデックスV3__add_fifo_tracking.sql- FIFO損益追跡カラム追加V4__add_trade_history_indexes.sql- TradeHistory検索最適化インデックス
アプリケーション起動時に未適用のマイグレーションが自動的に実行されます。
プロジェクトルートの quick_test.sh を使用して様々な機能をテストできます:
# 基本的な使用方法
./quick_test.sh [コマンド] [オプション]
# 利用可能なコマンド
./quick_test.sh market-buy # 成行買い注文
./quick_test.sh market-sell # 成行売り注文
./quick_test.sh limit-buy [PRICE] # 指値買い注文
./quick_test.sh board [SYMBOL] # 板情報取得
./quick_test.sh order-list [SYMBOL] # 注文中の注文リスト取得
./quick_test.sh history [PAGE] [SIZE] [SYMBOL] # 約定履歴取得(FILLED/PARTIAL_FILLのみ)
./quick_test.sh history-all [PAGE] [SIZE] [SYMBOL] # 全約定履歴取得(デバッグ用)
./quick_test.sh all-history [PAGE] [SIZE] [SYMBOL] # 全体約定履歴取得(全ユーザー)
./quick_test.sh volume [SYMBOL] [FROM_TIME] [TO_TIME] # 約定量計算
./quick_test.sh trade-insert [SYMBOL] [PRICE] [QUANTITY] [SIDE] # トレード挿入
./quick_test.sh full-test # フルテスト実行注文リストテストの例:
# 全銘柄の注文中の注文を取得
./quick_test.sh order-list
# 特定銘柄の注文中の注文を取得
./quick_test.sh order-list B_FX_BTCJPY約定履歴テストの例:
# 最新10件の約定履歴
./quick_test.sh history
# 最新5件の約定履歴
./quick_test.sh history 0 5
# B_FX_BTCJPYの最新3件
./quick_test.sh history 0 3 B_FX_BTCJPY
# 2ページ目(6-10件目)
./quick_test.sh history 1 5約定量計算テストの例:
# 特定銘柄の今日の約定量
./quick_test.sh volume B_FX_BTCJPY 2025-06-30T00:00:00 2025-06-30T23:59:59
# 過去2時間の約定量
./quick_test.sh volume G_FX_BTCJPY 2025-06-30T10:00:00 2025-06-30T12:00:00
# 全銘柄の約定量(今日)
./quick_test.sh volume ALL 2025-06-30T00:00:00 2025-06-30T23:59:59
# デフォルトパラメータで実行
./quick_test.sh volumeトレード挿入テストの例:
# BUY注文でのトレード挿入(板マッチング確認)
./quick_test.sh trade-insert B_FX_BTCJPY 15525000 0.02 BUY
# SELL注文でのトレード挿入
./quick_test.sh trade-insert B_FX_BTCJPY 15520000 0.01 SELL
# 実行前後の板状態を比較表示
./quick_test.sh trade-insert G_FX_BTCJPY 5000000 0.1 BUY
# デフォルトパラメータで実行
./quick_test.sh trade-insert本アプリケーションはPostgreSQLを使用してすべての取引データを永続化します:
PostgreSQLテーブル:
executions- 約定履歴users- ユーザー認証情報positions- ポジション情報trade_history- 取引履歴- FIFO損益追跡カラム (v3以降):
open_close- OPEN/CLOSE区分profit_loss- FIFO方式の損益matched_open_exec_ids- マッチしたOpen約定IDのJSON配列cl_ord_id- 注文ID
- FIFO損益追跡カラム (v3以降):
market_board_snapshots- 板情報スナップショット(1秒ごとに記録)
設定方法 (application.properties):
# PostgreSQL Configuration
app.database.type=postgresql
spring.datasource.url=jdbc:postgresql://localhost:5432/exch_sim
spring.datasource.username=postgres
spring.datasource.password=postgres123
spring.jpa.hibernate.ddl-auto=updateオプション機能 - BigQuery連携: 現在は無効化されていますが、大規模データ保存が必要な場合はBigQuery連携を有効化できます:
app.data-migration.bigquery-enabled=true
app.auth.bigquery-enabled=true主要なテストケース:
- 注文処理ロジック
- 約定マッチング
- 板情報管理
- 約定結果配信
- 約定履歴永続化
- ポジション管理
- FIFO方式損益追跡
- OPEN/CLOSE自動判定
- FIFO充当ロジック
- 部分約定の累積損益計算
- MarketMaker機能
- 権限制御
- Cash/FX取引制限
- エラーハンドリング
src/main/java/com/ys/exch_sim/
├── domain/
│ ├── controller/ # REST APIエンドポイント
│ ├── service/ # ビジネスロジック
│ ├── dto/ # データ転送オブジェクト
│ ├── config/ # 設定管理
│ ├── market_board/ # 板管理
│ ├── order_exec/ # 注文・約定管理
│ ├── position/ # ポジション・損益管理
│ └── message/ # メッセージフィールド
├── security/ # 認証・認可・権限制御
└── infra/ # インフラストラクチャ
# 1. ユーザー登録
curl -X POST http://localhost:8080/api/auth/signup \
-H "Content-Type: application/json" \
-d '{"username": "trader001", "password": "pass123"}'
# 2. ログイン
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "trader001", "password": "pass123"}'
# 3. FX商品で買い注文
curl -X POST http://localhost:8080/api/orders/new \
-H "Authorization: Bearer <JWT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"symbol": "G_FX_BTCJPY", "price": 5000000, "quantity": 1, "side": "BUY", "ordType": "LIMIT", "tif": "GTC"}'
# 4. ポジション確認
curl -X GET http://localhost:8080/api/positions/summary \
-H "Authorization: Bearer <JWT_TOKEN>"# 1. MarketMaker権限でログイン
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "marketmaker1", "password": "mmpass123"}'
# 2. 一括注文投入(既存注文を自動キャンセル)
curl -X POST http://localhost:8080/api/market-make/orders \
-H "Authorization: Bearer <MARKET_MAKER_JWT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"symbol": "G_FX_BTCJPY",
"bidLevels": [
{"price": 4995000, "quantity": 1},
{"price": 4990000, "quantity": 2}
],
"askLevels": [
{"price": 5005000, "quantity": 1},
{"price": 5010000, "quantity": 2}
]
}'このプロジェクトはMITライセンスの下で公開されています。
A comprehensive financial exchange system simulator that provides order placement/cancellation, execution processing, position management, and market making capabilities.
- Order Management: Place and cancel limit/market orders
- Execution Processing: Real-time order matching
- Order Book Information: Price and quantity information for buy/sell orders
- Execution History Management: Paginated execution history with PostgreSQL database persistence
- Position Management: Trade history, P&L calculation, and portfolio management
- Market Making: MARKET_MAKER exclusive bulk order functionality
- Instrument Type Management: Cash (spot) and FX (futures) trading restrictions
- JWT Authentication: Secure API access
- Role-based Access Control: API restrictions by user roles
- Java: 17+
- Spring Boot: 3.x
- Spring Security: JWT authentication & authorization
- Spring AOP: Permission checking
- PostgreSQL: Main data storage (execution history, positions, user authentication)
- Spring Data JPA: Database access layer
- Async Processing: Heavy initialization processes with @Async
- Gradle: Build tool
- JUnit 5: Testing framework
- Google BigQuery (Optional): Large-scale data storage for production (currently disabled)
- Short Selling Prohibited: Sell orders exceeding held positions are rejected
- Examples: G_BTCJPY, B_BTCJPY, TESTJPY
- Free Trading: Trading can start from either buy or sell side
- Examples: G_FX_BTCJPY, B_FX_BTCJPY
- Basic order and trading functions
- Position checking and trade history
- All USER permissions plus:
- Market make bulk order functionality
- Bulk cancellation and re-placement of existing orders
- Automatic Calculation: Positions and P&L automatically updated on execution
- Realized P&L: Profit/loss from completed trades
- Unrealized P&L: Mark-to-market P&L based on current prices
- Trade History: Detailed record of all executions
- Accurate P&L Calculation: Average prices automatically reset when positions become flat or reverse
- New trades after flat positions are not affected by past average prices
- Position reversals (Long→Short, Short→Long) are managed as new positions
- Realized P&L accurately matches actual trade prices
- Atomic Processing: Cancel existing orders → place new orders atomically
- Single-threaded: Symbol-level locking ensures MarketBoard consistency
- Bulk Management: Efficient management of orders by user and symbol
- Cash: Automatically rejects sell orders exceeding held positions
- FX: No restrictions, free buying and selling
| Symbol | Type | Description | Price Precision | Quantity Precision |
|---|---|---|---|---|
| G_BTCJPY | Cash | Bitcoin Spot | 1 | 1000 |
| G_FX_BTCJPY | FX | Bitcoin Futures | 1 | 1000 |
| B_BTCJPY | Cash | Bitcoin Spot | 1 | 1000 |
| B_FX_BTCJPY | FX | Bitcoin Futures | 1 | 1000 |
| TESTJPY | Cash | Test Spot | 1 | 1000 |
This project is released under the MIT License.