【AWS】bitbank Stop注文実装
1. 使用するサービス
(AWS)
- Lambda
- IAM
- SSM
- DynamoDB
(外部サービス)
- Bitbank API
2.概要
bitbank APIにはデフォルトで逆指値注文など特殊な注文方法がありあません。今回はDynamoDBを使用して事前に設定した価格を超えるまたは下がった場合にMarket注文を行う仕組みを実装します。
3. 実装
3-1. DynamoDBの中身
テーブルを事前に作成しておきます。PrimaryKeyをcoin_pair、SortKeyをPriceにします。bitflyer 自動注文などで作成したテーブルと基本内容は同じです。
buffalokusojima.hatenablog.com
3-2. Lambdaの実装
const AWS = require('aws-sdk'); const ssm = new (require('aws-sdk/clients/ssm'))(); const request = require('request'); const momentTimezone = require('moment-timezone'); const lambda = new AWS.Lambda(); AWS.config.update({region: 'ap-northeast-1'}); const ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'}); exports.handler = (event, context, callback) => { var XRP_JPY = process.env['XRP_JPY']; var dbData; getDatafromDynamoDB('stop_check_bitbank', callback) .then(function(data){ if(data.Items.length == 0){ console.log("stop check data not set"); callback(null, { statusCode: 200, body: JSON.stringify({message: "price check data not set"}), headers: {"Content-type": "application/json"} }); return; } dbData = data.Items; console.log(dbData); const method = "GET" const path = "/xrp_jpy/depth" var option = { url: 'https://public.bitbank.cc' + path, method: method, headers: { 'Content-Type': 'application/json' } } sendRequest(option,callback) .then(function(data){ if(data.response.statusCode != 200){ console.error("Error:",data.response); callback(null, { statusCode: data.response.statusCode, body: JSON.stringify({message: data.response}), headers: {"Content-type": "application/json"} }); return; } var price_data = JSON.parse(data.body); if(price_data == null){ console.log("price data not Found"); callback(null, { statusCode: 403, body: JSON.stringify({message: 'No Data Found'}), headers: {"Content-type": "application/json"} }); return; } price_data = price_data.data; console.log(price_data); dbData.forEach(function(item){ if(item.side.S == "buy"){ if(item.price.N < Number(price_data.asks[0][0])){ executeOrder(item,callback); } }else if(item.side.S == "sell"){ if(item.price.N > Number(price_data.bids[0][0])){ executeOrder(item,callback); } } }); callback(null, { statusCode: 200, body: JSON.stringify({message: 'No Order Executed'}), headers: {"Content-type": "application/json"} }); return; }); }); function executeOrder(item,callback){ var param = { "coin_pair": item.coin_pair.S, "price": item.price.N, "size": item.size.N, "side": item.side.S, "type": "market" }; console.log("order will be executed:", param); var payload = param; payload = JSON.stringify({body:JSON.stringify(payload)}); callLambda(payload, callback); deleteDataFromDynamoDB('stop_check_bitbank', param, callback); } function getDatafromDynamoDB(table_name, callback){ return new Promise(function (resolve) { var params = { ExpressionAttributeValues: { ':c': {S: 'xrp_jpy'} }, KeyConditionExpression: 'coin_pair = :c', ProjectionExpression: 'coin_pair, price, side, size', TableName: table_name }; // Call DynamoDB to add the item to the table ddb.query(params, function(err, data) { if (err) { console.log("Error", err); callback(null, { statusCode: 401, body: JSON.stringify({message: err.toString()}), headers: {"Content-type": "application/json"} }); resolve(null); return; } resolve(data); }); }); } function deleteDataFromDynamoDB(table_name, params, callback){ var params = { TableName: table_name, Key: { 'coin_pair' : {S: params.coin_pair}, 'price' : {N: params.price} } }; ddb.deleteItem(params, function(err, data){ if (err) { console.log("Error", err); callback(null, { statusCode: 400, body: JSON.stringify({message: err}), headers: { "Content-type": "application/json" } }); return; } console.log("Success"); params = params.Key; callback(null,{ statusCode: 200, body: JSON.stringify({message: 'DeleteItem Success: {coin_pair: ' + params.coin_pair.S + ' price: ' + params.price.N }), headers: { "Content-type": "application/json" } }); return; }); } function getParameterFromSystemManager(apikey_name, callback) { return new Promise(function (resolve) { var apikey = process.env[apikey_name]; if(!apikey || typeof apikey == undefined){ // Fetches a parameter called REPO_NAME from SSM parameter store. // Requires a policy for SSM:GetParameter on the parameter being read. var params = { Name: apikey_name, /* required */ WithDecryption:true }; ssm.getParameter(params, function(err, apikey) { if (err){ console.error(err.stack); callback(null,{ statusCode: 500, body: JSON.stringify({message: err.toString()}), headers: {"Content-type": "application/json"} }); resolve(null); return; } process.env[apikey_name] = apikey.Parameter.Value; resolve(apikey.Parameter.Value); }); }else resolve(apikey); }); } function callLambda(payload, callback){ return new Promise(function (resolve) { var params = { FunctionName: "bitbank-project-pipeline-stac-ControlOrderFunction-114U1VFTL0EE0", InvocationType: "RequestResponse", Payload: payload } console.log(params); lambda.invoke(params, function(error, res){ if(error){ console.error(error); callback(null,{ statusCode: 500, body: JSON.stringify({message: error.toString()}), headers: {"Content-type": "application/json"} }); resolve(null); } resolve(res); }); }); } function sendRequest(option, callback){ return new Promise(function (resolve) { request(option, function(error, response, body){ if(error){ console.error(error); callback(null,{ statusCode: 500, body: JSON.stringify({message: error.toString()}), headers: {"Content-type": "application/json"} }); resolve(null); } var data = {response, body} resolve(data); }); }); } };
CloudWatchで定期的にキックすることでSTOP注文を実現します。