各取引所のAPI利用方法の紹介は、今回のFISCOが最後となります。
前提・条件
開発実行環境は、24時間システムを稼働することも考慮し、「お名前.comデスクトップクラウド」の仮想マシンを使って作成~稼働まで行っています。PowerShellの基本的な文法、使い方については解説を省略しています。APIを公開している仮想通貨取引所の口座開設が完了しているものとします。口座開設方法については省略しています。システムトレードを行うことによって必ずしも利益が出るという保証はありません。.NET Framework で TLS1.1 および 1.2 を有効化する
PowerShellで使用される暗号化プロトコルは以下で確認することができます。
[Net.ServicePointManager]::SecurityProtocolSsl3, Tls
既定ではSSL3、TLS(1.0)が利用可能となっています。しかし最近では暗号化プロトコルにTLS 1.2を必須とするサイトが増えてきており、仮想通貨取引所のAPIも同様となっています。既定の設定では.NET Frameworkを利用しているInvoke-WebRequestコマンドを実行するとエラーとなる場合があります。Invoke-WebRequestコマンドでTLS 1.2を利用するようにあらかじめ設定をしておきましょう。
1つ目の方法としてはInvoke-WebRequestコマンドを呼び出す前にTLS 1.2で通信することを明記します。こちらはスクリプト毎にその都度明記する必要があります。
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12$res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body -ContentType "application/json"
もう1つの方法としては、以下のコマンドを実行して、レジストリでTLS 1.2で通信を既定値として設定しておきます。
Set-ItemProperty -Path HKLM:\\SOFTWARE\Microsoft\.NETFramework\v4.0.30319 -Name SchUseStrongCrypto -Value 1"
いずれかの対策を事前に行うことで、Invoke-WebRequestコマンドでAPIを利用する場合のエラーを回避することが可能です。
参考:.NET Framework で TLS1.1 および 1.2 を有効化する方法
FISCO APIの使い方
FISCOでは仮想通貨の現物取引においてAPIを利用した取引注文を行うことが可能です。APIを利用した場合、指値のみの注文となるのでご注意ください。
APIの仕様自体はZaifの現物取引と非常に似ています。
まずはコードの一覧です。以降抜粋したコードについて説明している箇所は、一覧の行番号を参照しています。
Function Get-Price($SiteName){
    Switch ($SiteName)
    {
        "bitFlyer"{$Url = "https://api.bitflyer.jp/v1/getboard?product_code=BTC_JPY"}
        "bitFlyerFX"{$Url = "https://api.bitflyer.jp/v1/getboard?product_code=FX_BTC_JPY"}
        {($_ -eq "Zaif") -Or ($_ -eq "ZaifFX")}{$Url = "https://api.zaif.jp/api/1/depth/btc_jpy"}
        "bitbank"{$Url = "https://public.bitbank.cc/btc_jpy/depth"}
        {($_ -eq "QUOINE") -Or ($_ -eq "QUOINEFX")}{$Url = "https://api.quoine.com/products/5/price_levels"}
        "BTCBOX"{$Url = "https://www.btcbox.co.jp/api/v1/depth/"}
        "FISCO"{$Url = "https://api.fcce.jp/api/1/depth/btc_jpy"}
    }
    [int]$AskPrice = 0
    [int]$BidPrice = 0
    [single]$AskSize = 0
    [single]$BidSize = 0
    $res = Invoke-RestMethod $Url -Method GET
    Switch ($SiteName)
    {
        {($_ -eq "bitFlyer") -Or ($_ -eq "bitFlyerFX")}{ 
            $AskPrice = $res.asks[0].price
            $BidPrice = $res.bids[0].price
            $AskSize = $res.asks[0].size
            $BidSize = $res.bids[0].size
        }
        {($_ -eq "Zaif") -Or ($_ -eq "ZaifFX")}{
            $AskPrice = $res.asks[0][0]
            $BidPrice = $res.bids[0][0]
            $AskSize = $res.asks[0][1]
            $BidSize = $res.bids[0][1]
        }
        "bitbank"{
            $AskPrice = $res.data.asks[0][0]
            $BidPrice = $res.data.bids[0][0]
            $AskSize = $res.data.asks[0][1]
            $BidSize = $res.data.bids[0][1]
        }
        {($_ -eq "QUOINE") -Or ($_ -eq "QUOINEFX")}{  
            $AskPrice = $res.sell_price_levels[0][0]
            $BidPrice = $res.sell_price_levels[0][0]
            $AskSize = $res.buy_price_levels[0][1]
            $BidSize = $res.buy_price_levels[0][1]
        }
        "BTCBOX"{
            $AskPrice =$res.asks[$res.asks.Count-1][0]
            $BidPrice = $res.bids[0][0]
            $AskSize = $res.asks[$res.asks.Count-1][1]
            $BidSize = $res.bids[0][1]
        }
        "FISCO"{
            $AskPrice = $res.asks[0][0]
            $BidPrice = $res.bids[0][0]
            $AskSize = $res.asks[0][1]
            $BidSize = $res.bids[0][1]
        }
    }
    $objPs = New-Object PSCustomObject
    $objPs | Add-Member -NotePropertyMembers @{Name = $SiteName}
    $objPs | Add-Member -NotePropertyMembers @{AskPrice =  $AskPrice}
    $objPs | Add-Member -NotePropertyMembers @{AskSize = $AskSize.ToString("0.00000")}
    $objPs | Add-Member -NotePropertyMembers @{BidPrice = $BidPrice}
    $objPs | Add-Member -NotePropertyMembers @{BidSize = $BidSize.ToString("0.00000")}
    $objPs | FT
}
Function Get-Keys($SiteName){
    Return (Get-Content (".\Keys.json")  -Encoding UTF8 -Raw | ConvertFrom-Json) | Where Site -eq $SiteName
}
Function Get-Header($SiteName, $Query){
    $Keys = Get-Keys $SiteName
    $APIKey = $Keys.APIKey
    $SecretKey = $Keys.SecretKey 
    Switch ($SiteName)
    {        
        {($_ -eq "bitFlyer") -Or ($_ -eq "bitFlyerFX")}{           
            $Nonce = ([DateTimeOffset](Get-Date)).ToUnixTimeMilliseconds()
            $Query = $Nonce.ToString() + $Query
            $KeyData = [System.Text.Encoding]::UTF8.GetBytes($SecretKey)
            $QueryData = [System.Text.Encoding]::UTF8.GetBytes($Query)
            Add-Type -AssemblyName System.Security
            $HMAC = New-Object System.Security.Cryptography.HMACSHA256
            $HMAC.Key = $KeyData
            $HMACHash = $HMAC.ComputeHash($QueryData)
            $Sign = [System.BitConverter]::ToString($HMACHash).ToLower().Replace("-", "")
            $Header = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Header.Add("ACCESS-KEY", "$APIKey")
            $Header.Add("ACCESS-TIMESTAMP", "$Nonce")
            $Header.Add("ACCESS-SIGN", "$Sign")
            Return $Header
        }
        {($_ -eq "Zaif") -Or ($_ -eq "ZaifFX")}{
            Add-Type -AssemblyName System.Net.Http
            $Content = New-Object System.Net.Http.FormUrlEncodedContent($Query)            
            $Query = $Content.ReadAsStringAsync().Result
            $KeyData = [System.Text.Encoding]::UTF8.GetBytes($SecretKey)
            $QueryData = [System.Text.Encoding]::UTF8.GetBytes($Query)
            Add-Type -AssemblyName System.Security
            $HMAC = New-Object System.Security.Cryptography.HMACSHA512
            $HMAC.Key = $KeyData
            $HMACHash = $HMAC.ComputeHash($QueryData)
            $Sign = [System.BitConverter]::ToString($HMACHash).ToLower().Replace("-", "")
            $Header = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Header.Add("key", "$APIKey")
            $Header.Add("Sign", "$Sign")
            Return $Header 
        }
        "bitbank"{
            $Nonce = ([DateTimeOffset](Get-Date)).ToUnixTimeMilliseconds()
            $Url ="https://api.bitbank.cc/v1/user/assets"  
            $Query = $Nonce.ToString() + $Query
            $KeyData =  [System.Text.Encoding]::UTF8.GetBytes($SecretKey)
            $QueryData =  [System.Text.Encoding]::UTF8.GetBytes($Query)
            Add-Type -AssemblyName System.Security
            $HMAC = New-Object System.Security.Cryptography.HMACSHA256
            $HMAC.Key = $KeyData
            $HMACHash = $HMAC.ComputeHash($QueryData)
            $Sign = [System.BitConverter]::ToString($HMACHash).ToLower().Replace("-", "")
            $Header = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Header.Add("ACCESS-KEY", "$APIKey")
            $Header.Add("ACCESS-NONCE", "$Nonce")
            $Header.Add("ACCESS-SIGNATURE", "$Sign")
            Return $Header
        }
        {($_ -eq "QUOINE") -Or ($_ -eq "QUOINEFX")}{  
            $Nonce = ([DateTimeOffset](Get-Date)).ToUnixTimeMilliseconds()
            $HeaderByte = [System.Text.Encoding]::UTF8.GetBytes('{"alg":"HS256","typ":"JWT"}')
            $HeaderData = [Convert]::ToBase64String($HeaderByte)
            $BodyByte = [System.Text.Encoding]::UTF8.GetBytes('{"path":"'+$Query+'","nonce":"'+$Nonce+'","token_id":"'+$APIKey+'"}')
            $BodyData = [Convert]::ToBase64String($BodyByte)
            $HeaderBody = $HeaderData + "." + $BodyData
            $KeyData =  [System.Text.Encoding]::UTF8.GetBytes($SecretKey)
            $HeaderBodyData =  [System.Text.Encoding]::UTF8.GetBytes($HeaderBody)
            Add-Type -AssemblyName System.Security
            $HMAC = New-Object System.Security.Cryptography.HMACSHA256
            $HMAC.Key = $KeyData
            $HMACHash = $HMAC.ComputeHash($HeaderBodyData)
            $SignData = [Convert]::ToBase64String($HMACHash)
            $Sign = $HeaderBody + "." + $SignData
            $Header = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Header.Add("X-Quoine-API-Version", "2")
            $Header.Add("X-Quoine-Auth", "$Sign")
            Return $Header
        }
        "BTCBOX"{
            $Nonce = ([DateTimeOffset](Get-Date)).ToUnixTimeMilliseconds()           
            $Body = $Query
            $Body.Add("key", "$APIKey")
            $Body.Add("nonce", "$nonce")
            Add-Type -AssemblyName System.Net.Http
            $Content =  New-Object System.Net.Http.FormUrlEncodedContent($Body)
            $Query =  $Content.ReadAsStringAsync().Result
            $SecretKeyData = [System.Text.Encoding]::UTF8.GetBytes($SecretKey)
            Add-Type -AssemblyName System.Security
            $MD5 = New-Object System.Security.Cryptography.MD5CryptoServiceProvider
            $MD5Hash = $MD5.ComputeHash($SecretKeyData)
            $KeyString = [System.BitConverter]::ToString($MD5Hash).ToLower().Replace("-", "")
            $KeyData =  [System.Text.Encoding]::UTF8.GetBytes($KeyString)
            $QueryData =  [System.Text.Encoding]::UTF8.GetBytes($Query)
            Add-Type -AssemblyName System.Security
            $HMAC = New-Object System.Security.Cryptography.HMACSHA256
            $HMAC.Key = $KeyData
            $HMACHash = $HMAC.ComputeHash($QueryData)
            $Signature = [System.BitConverter]::ToString($HMACHash).ToLower().Replace("-", "")
            $Body.Add("signature", "$Signature")
            Return $Body 
        }
        "FISCO"{
            Add-Type -AssemblyName System.Net.Http
            $Content = New-Object System.Net.Http.FormUrlEncodedContent($Query)            
            $Query = $Content.ReadAsStringAsync().Result
            $KeyData = [System.Text.Encoding]::UTF8.GetBytes($SecretKey)
            $QueryData = [System.Text.Encoding]::UTF8.GetBytes($Query)
            Add-Type -AssemblyName System.Security
            $HMAC = New-Object System.Security.Cryptography.HMACSHA512
            $HMAC.Key = $KeyData
            $HMACHash = $HMAC.ComputeHash($QueryData)
            $Sign = [System.BitConverter]::ToString($HMACHash).ToLower().Replace("-", "")
            $Header = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Header.Add("key", "$APIKey")
            $Header.Add("Sign", "$Sign")
            Return $Header 
        }
    }
}
Function Get-Asset($SiteName, $AssetName){
    [single]$Asset = 0
    Switch ($SiteName){
        "bitFlyer"{
            $Url ="https://api.bitflyer.jp/v1/me/getbalance"
            $Query = "GET/v1/me/getbalance"
            $Header = Get-Header $SiteName $Query
            $res = Invoke-RestMethod $Url -Method GET -Headers $Header
            $Asset =  ($res | Where currency_code -eq "$AssetName").amount
        }
        "bitFlyerFX"{
            $Url ="https://api.bitflyer.jp/v1/me/getcollateral"
            $Query = "GET/v1/me/getcollateral"
            $Header = Get-Header $SiteName $Query
            $res = Invoke-RestMethod $Url -Method GET -Headers $Header            
            If($AssetName -eq "jpy"){$Asset =  $res.collateral}Else{$Asset = 0}
        }
        {($_ -eq "Zaif") -Or ($_ -eq "ZaifFX")}{
            $Url = "https://api.zaif.jp/tapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "get_info")
            $Header = Get-Header $SiteName $Body
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body
            $Asset = $res.return.funds.($AssetName)
        }
        "bitbank"{
            $Url ="https://api.bitbank.cc/v1/user/assets"
            $Query = "/v1/user/assets"
            $Header = Get-Header $SiteName $Query
            $res = Invoke-RestMethod $Url -Method GET -Headers $Header
            $Asset =  ($res.data.assets | Where asset -eq "$AssetName").free_amount
        }
        {($_ -eq "QUOINE") -Or ($_ -eq "QUOINEFX")}{  
            $Url = "https://api.quoine.com/accounts/balance/"       
            $Query = "/accounts/balance"    
            $Header =  Get-Header $SiteName $Query
            $res = Invoke-RestMethod $Url -Method GET -Headers $Header
            $Asset = ($res | Where currency -eq "$AssetName").balance
        }
        "BTCBOX"{
            $Url ="https://www.btcbox.co.jp/api/v1/balance/"
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body = Get-Header $SiteName $Body
            $res = Invoke-RestMethod $Url -Method POST -Body $Body
            $Asset = $res.($AssetName + "_balance")
        }
        "FISCO"{
            $Url = "https://api.fcce.jp/tapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "get_info")
            $Header = Get-Header $SiteName $Body
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body
            $Asset = $res.return.funds.($AssetName)
        }
    }
    If($AssetName -eq "jpy"){
        Return $Asset.ToString("#,0")
    }Else{
        Return $Asset.ToString("#,0.00000000")
    }
    Return $Asset
}
Function Set-Order($SiteName, $Side, $Type, $Price, $Amount){
    Switch ($SiteName)
    {
        "bitFlyer"{
            $Url ="https://api.bitflyer.jp/v1/me/sendchildorder"
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("product_code", "BTC_JPY")
            $Body.Add("side", $Side.ToUpper())
            $Body.Add("child_order_type",  $Type.ToUpper())
            $Body.Add("price",  "$Price")
            $Body.Add("size", "$Amount")
            $Body = ConvertTo-JSON $Body 
            $Query = "POST/v1/me/sendchildorder" + $Body
            $Header = Get-Header $SiteName $Query
            Try{
                $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body -ContentType "application/json"
            }Catch{
                Write-Host "Oder Error! " $_.ErrorDetails.Message -ForegroundColor Red
                Return 0            
            }
            Return $res.child_order_acceptance_id
        }
        "bitFlyerFX"{
            $Url ="https://api.bitflyer.jp/v1/me/sendchildorder"
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("product_code", "FX_BTC_JPY")
            $Body.Add("side", $Side.ToUpper())
            $Body.Add("child_order_type",  $Type.ToUpper())
            $Body.Add("price",  "$Price")
            $Body.Add("size", "$Amount")
            $Body = ConvertTo-JSON $Body 
            $Query = "POST/v1/me/sendchildorder" + $Body
            $Header = Get-Header $SiteName $Query
            Try{
                $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body -ContentType "application/json"
            }Catch{
                Write-Host "Oder Error! " $_.ErrorDetails.Message -ForegroundColor Red
                Return 0            
            }
            Return $res.child_order_acceptance_id
        }
        "Zaif"{
            $Url = "https://api.zaif.jp/tapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "trade")            
            $Body.Add("currency_pair", "btc_jpy")
            If($Side -eq "sell"){$Side = "ask"}ElseIf($Side -eq "buy"){$Side = "bid"}
            $Body.Add("action", "$Side")
            $Body.Add("price", "$Price")
            $Body.Add("amount", "$Amount")
            $Header = Get-Header $SiteName $Body
            Try{      
                $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body
                If($res.success -eq 0){
                    Write-Host "Oder Error! " $res.error -ForegroundColor Red
                    Return 0
                }Else{
                    Return 1
                }
            }Catch{
                Write-Host "Oder Error! " $_.ErrorDetails.Message -ForegroundColor Red
                Return 0
            }
        }
        "ZaifFX"{
            $Url = "https://api.zaif.jp/tlapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "create_position")
            $Body.Add("type", "margin")
            $Body.Add("currency_pair", "btc_jpy")
            If($Side -eq "sell"){$Side = "ask"}ElseIf($Side -eq "buy"){$Side = "bid"}
            $Body.Add("action", "$Side")
            $Body.Add("price", "$Price")
            $Body.Add("amount", "$Amount")
            $Body.Add("leverage", "2")
            $Header = Get-Header $SiteName $Body
            Try{          
                $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body
                If($res.success -eq 0){
                    Write-Host "Oder Error! " $res.error -ForegroundColor Red
                    Return 0
                 }Else{
                    Return $res.return.leverage_id
                }
            }Catch{
                Write-Host "Oder Error! " $_.ErrorDetails.Message -ForegroundColor Red
                Return 0
            }
        }
        "bitbank"{
            $Url ="https://api.bitbank.cc/v1/user/spot/order"
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("pair", "btc_jpy")
            $Body.Add("side", "$Side")
            $Body.Add("type", "$Type")
            $Body.Add("price", "$Price")
            $Body.Add("amount", "$Amount")     
            $Body = ConvertTo-JSON $Body   
            $Header =  Get-Header $SiteName $Body
            $res =Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body -ContentType "application/json"            
            If($res.success -eq 0){Write-Host "Oder Error!" -ForegroundColor Red; Return 0}
            Return $res.data.order_id
        }
        "QUOINE" {
            $Url = "https://api.quoine.com/orders/"
            $Query = "/orders/"    
            $Header = Get-Header $SiteName $Query
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("product_id", "5")
            $Body.Add("side", "$Side")
            $Body.Add("order_type", "$Type")  
            $Body.Add("price", "$Price")
            $Body.Add("quantity", "$Amount")
            $Body = ConvertTo-JSON $Body
            Try{$res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body -ContentType "application/json"}Catch{Write-Host "Oder Error!" -ForegroundColor Red; Return 0}
            Return $res.id
        }
        "QUOINEFX" {
            $Url = "https://api.quoine.com/orders/"
            $Query = "/orders/"    
            $Header = Get-Header "QUOINE" $Query
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("product_id", "5")
            $Body.Add("side", "$Side")
            $Body.Add("order_type", "$Type")  
            $Body.Add("price", "$Price")
            $Body.Add("quantity", "$Amount")
            $Body.Add("leverage_level", "2")      
            $Body.Add("funding_currency", "JPY") 
            $Body = ConvertTo-JSON $Body
            Try{$res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body -ContentType "application/json"}Catch{Write-Host "Oder Error!" -ForegroundColor Red; Return 0}
            Return $res.id
        }
        "BTCBOX"{
            $Url ="https://www.btcbox.co.jp/api/v1/trade_add/"
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("type", "$Side")
            $Body.Add("price", "$Price")
            $Body.Add("amount", "$Amount")
            $Body = Get-Header $SiteName $Body
            $res = Invoke-RestMethod $Url -Method POST -Body $Body            
            If($res.result -eq $False){Write-Host "Oder Error!" -ForegroundColor Red; Return 0}
            Return $res.id
        }
        "FISCO"{
            $Url = "https://api.fcce.jp/tapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "trade")            
            $Body.Add("currency_pair", "btc_jpy")
            If($Side -eq "sell"){$Side = "ask"}ElseIf($Side -eq "buy"){$Side = "bid"}
            $Body.Add("action", "$Side")
            $Body.Add("price", "$Price")
            $Body.Add("amount", "$Amount")
            $Header = Get-Header $SiteName $Body
        }
        Try{      
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body
            If($res.success -eq 0){
                Write-Host "Oder Error! " $res.error -ForegroundColor Red
                Return 0
            }Else{
                Return 1
            }
        }Catch{
            Write-Host "Oder Error! " $_.ErrorDetails.Message -ForegroundColor Red
            Return 0
        }
    }    
}
Function Get-Order($SiteName){
    Switch ($SiteName)
    {        
        "bitFlyer"{
            $Url ="https://api.bitflyer.jp/v1/me/getchildorders?product_code=BTC_JPY&child_order_state=ACTIVE"
            $Query = "GET/v1/me/getchildorders?product_code=BTC_JPY&child_order_state=ACTIVE"
            $Header = Get-Header $SiteName $Query
            $res = Invoke-RestMethod $Url -Method GET -Headers $Header
            Return $res
        }
        "bitFlyerFX"{
            $Url ="https://api.bitflyer.jp/v1/me/getchildorders?product_code=FX_BTC_JPY&child_order_state=ACTIVE"
            $Query = "GET/v1/me/getchildorders?product_code=FX_BTC_JPY&child_order_state=ACTIVE"
            $Header = Get-Header $SiteName $Query
            $res = Invoke-RestMethod $Url -Method GET -Headers $Header
            Return $res
        }
         "Zaif"{
            $Url = "https://api.zaif.jp/tapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "active_orders")  
            $Header = Get-Header $SiteName $Body    
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body
            Return $res.return
        }
        "ZaifFX"{
            $Url = "https://api.zaif.jp/tlapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "active_positions")
            $Body.Add("type", "margin")
            $Header = Get-Header $SiteName $Body
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body
            Return $res.return
        }
        "bitbank"{
            $Url ="https://api.bitbank.cc/v1/user/spot/active_orders"
            $Query = "/v1/user/spot/active_orders"    
            $Header = Get-Header $SiteName $Query
            $res =Invoke-RestMethod $Url -Method GET -Headers $Header
            Return $res.data.orders        
        }
        {($_ -eq "QUOINE") -Or ($_ -eq "QUOINEFX")}{  
            $Url = "https://api.quoine.com/orders?funding_currency=JPY&product_id=5&status=live&with_details=1"
            $Query = "/orders?funding_currency=JPY&product_id=5&status=live&with_details=1"    
            $Header = Get-Header $SiteName $Query
            $res = Invoke-RestMethod $Url -Method GET -Headers $Header
            Return $res.models
        }
        "BTCBOX"{
            $Url ="https://www.btcbox.co.jp/api/v1/trade_list/"
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("since", "0")
            $Body.Add("type", "live")
            $Body = Get-Header $SiteName $Body
            $res = Invoke-RestMethod $Url -Method POST -Body $Body            
            Return $res        
        }
        "FISCO"{
            $Url = "https://api.fcce.jp/tapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "active_orders")  
            $Header = Get-Header $SiteName $Body    
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body
            Return $res.return
        }
    }
}
Function Cancel-Order($SiteName, $OrderID){
    Switch ($SiteName)
    {
        "bitFlyer"{     
            $Url ="https://api.bitflyer.jp/v1/me/cancelchildorder"
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("product_code", "BTC_JPY")
            $Body.Add("child_order_acceptance_id", "$OrderID")
            $Body = ConvertTo-JSON $Body 
            $Query = "POST/v1/me/cancelchildorder" + $Body
            $Header = Get-Header $SiteName $Query
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body -ContentType "application/json"
         }
        "bitFlyerFX"{     
            $Url ="https://api.bitflyer.jp/v1/me/cancelchildorder"
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("product_code", "FX_BTC_JPY")
            $Body.Add("child_order_acceptance_id", "$OrderID")
            $Body = ConvertTo-JSON $Body 
            $Query = "POST/v1/me/cancelchildorder" + $Body
            $Header = Get-Header $SiteName $Query
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body -ContentType "application/json"
         }
        "Zaif"{
            $Url = "https://api.zaif.jp/tapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "cancel_order")            
            $Body.Add("order_id", "$OrderID")
            $Header = Get-Header $SiteName $Body       
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body 
        }
        "ZaifFX"{
            $Url = "https://api.zaif.jp/tlapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "cancel_position")
            $Body.Add("leverage_id", "$OrderID")
            $Header = Get-Header $SiteName $Body
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body
        }
        "bitbank"{
            $Url ="https://api.bitbank.cc/v1/user/spot/cancel_order"
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("pair", "btc_jpy")
            $Body.Add("order_id", "$OrderID")
            $Body = ConvertTo-JSON $Body
            $Header =  Get-Header $SiteName $Body
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body -ContentType "application/json"
        }
        {($_ -eq "QUOINE") -Or ($_ -eq "QUOINEFX")}{
            $Url = "https://api.quoine.com/orders/$OrderID/cancel"
            $Query = "/orders/$OrderID/cancel"    
            $Header = Get-Header $SiteName $Query
            $res = Invoke-RestMethod $Url -Method PUT -Headers $Header
        }
        "BTCBOX"{
            $Url ="https://www.btcbox.co.jp/api/v1/trade_cancel/"
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("id", "$OrderID")
            $Body = Get-Header $SiteName $Body
            $res = Invoke-RestMethod $Url -Method POST -Body $Body
        }
        "FISCO"{
            $Url = "https://api.fcce.jp/tapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "cancel_order")            
            $Body.Add("order_id", "$OrderID")
            $Header = Get-Header $SiteName $Body       
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body 
        }
    }
    Return $res
}
Function Close-FX($SiteName, $CloseOrderID, $BtcPrice){
    Switch ($SiteName)
    {
        "bitFlyerFX"{
            #反対売買を行うことでポジションをクローズ
        }
        "ZaifFX"{
            $Url = "https://api.zaif.jp/tlapi"
            $res = Get-Order $SiteName
            ForEach($item In $res.return | Get-Member | ? MemberType -eq NoteProperty){
                $OrderID =$item.Name
                If($CloseOrderID -eq $OrderID){
                    $Price = $res.return.$OrderID.price
                    $Amount = $res.return.$OrderID.amount
                    $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
                    $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
                    $Body.Add("nonce", "$Nonce")
                    $Body.Add("method", "change_position")
                    $Body.Add("type", "margin")
                    $Body.Add("leverage_id", "$OrderID")
                    If($Price -ge $BtcPrice){$Body.Add("limit", "$BtcPrice")}Else{$Body.Add("stop", "$BtcPrice")}    
                    $Header = Get-Header Zaif $Body
                    $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body
                }
            }
        }
        "QUOINEFX"{
            $Amount = (Get-Order $SiteName | ? id -eq $CloseOrderID).quantity
            $Url = "https://api.quoine.com/trades/$OrderID/close"
            $Query = "/trades/$CloseOrderID/close"
            $Header = Get-Header QUOINE $Query
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"   
            $Body.Add("closed_quantity", "$Amount")       
            $Body = ConvertTo-JSON $Body
            $res = Invoke-RestMethod -Method POST $Url -Headers $Header -Body $Body -ContentType "application/json"      
        }
    }
    Return $res
}
#取引所 (bitFlyer, bitFlyerFX, Zaif, ZaifFX, bitbank, QUOINE, QUOINEFX, BTCBOX, FISCO)
    $SiteName = "FISCO"
#資産 (jpy, btc)
    $AssetName = "jpy"
#売買 (sell, buy)
    $Side = "buy"
#指値・成行 (limit, market)
    $Type = "limit"
#価格(FISCO:5円単位)
    $Price = 600000
#注文量 (bitFlyer>0.001, Zaif>0.0001, bitbank>0.001, QUOINE>0.001, BTCBOX>0.001, FISCO>0.0001)
    $Amount = 0.001
#ビットコイン価格
    $BtcPrice = 700000
#価格情報
    #Get-Price $SiteName
#資産情報
    #Get-Asset $SiteName $AssetName
#売買注文
    #$OrderID = Set-Order $SiteName $Side $Type $Price $Amount
#注文情報
    #Get-Order $SiteName
#注文取消し
    #Cancel-Order $SiteName $OrderID
#FXポジションクローズ
    #Close-FX $SiteName $OrderID $BtcPrice
(https://github.com/InvokeV/PSdeCoinEX/blob/master/Sample_07.ps1)
価格情報の取得:Get-Price
        "FISCO"{
            $AskPrice = $res.asks[0][0]
            $BidPrice = $res.bids[0][0]
            $AskSize = $res.asks[0][1]
            $BidSize = $res.bids[0][1]
        }
Public APIを使って板情報から売値(asks)と買値(bids)を取得します。FISCOの場合はhttps://api.fcce.jp/api/1/depth/btc_jpyこのURLからGetメソッドで直接ビットコインの価格情報が取得可能です。
Get-Price "FISCO"Name AskPrice AskSize BidPrice BidSize---- -------- ------- -------- -------FISCO 705450 0.03990 705075 0.04000
API KeyとSecret Keyの取得:Get-Keys
Private APIを利用するための公開鍵と秘密鍵を取得します。FISCOのサイトメニューから「アカウント」-「API Keys」を選択します。https://fcce.jp/accountこちらからAPIキーとシークレットキーを作成します。
APIキーの取得
Function Get-Keys($SiteName){
    Return (Get-Content (".\Keys.json")  -Encoding UTF8 -Raw | ConvertFrom-Json) | Where Site -eq $SiteName
}
キーを直接コード内に記述するのは避けたいので、Keys.jsonファイルとして別途保存しておきます。これまでと同様にjson形式で追加しておきます。
[  {    (省略)  },  {    "Site": "FISCO",    "APIKey": "取得したAPIキー",    "SecretKey": "取得したシークレットキー"  }]
(Keys.json)
Header情報の作成:Get-Header
取得したキーを使ってAPIの認証を行うためには、Invoke-RestMethodコマンドにHeaderとして認証情報を作成して渡す必要があります。FISCOの場合は必要な情報を文字列として連結して、SHA512ハッシュ関数を使ってシークレットキーで署名したものが認証に必要な情報となります。難しい話は読み飛ばしてもコードをそのまま再利用すれば使えるものを紹介します。
必要なHeader情報
項目値keyAPIキーsign署名値 ※1
※1:クエリー値(Nonce、メソッド、パラメーター等)を文字列として連結して、シークレットキーを使ってSHA512ハッシュ関数で署名したもの。
        "FISCO"{
            Add-Type -AssemblyName System.Net.Http
            $Content = New-Object System.Net.Http.FormUrlEncodedContent($Query)            
            $Query = $Content.ReadAsStringAsync().Result
            $KeyData = [System.Text.Encoding]::UTF8.GetBytes($SecretKey)
            $QueryData = [System.Text.Encoding]::UTF8.GetBytes($Query)
            Add-Type -AssemblyName System.Security
            $HMAC = New-Object System.Security.Cryptography.HMACSHA512
            $HMAC.Key = $KeyData
            $HMACHash = $HMAC.ComputeHash($QueryData)
            $Sign = [System.BitConverter]::ToString($HMACHash).ToLower().Replace("-", "")
            $Header = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Header.Add("key", "$APIKey")
            $Header.Add("Sign", "$Sign")
            Return $Header 
        }
    }
72: 作成したGet-KeysコマンドでAPIキーを読み込みます。171-178: Queryとして渡されたデータを、シークレットキーを使ってハッシュ関数で署名します。179: ハイフンを省いた文字列として取得します。180-183: Header情報を作成します。
資産情報の取得:Get-Asset
FISCOに保持している日本円とビットコインの残高を取得します。FISCOでは、ビットコイン以外の仮想通貨の取引が可能なので、他の通貨情報も取得可能です。
        "FISCO"{
            $Url = "https://api.fcce.jp/tapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "get_info")
            $Header = Get-Header $SiteName $Body
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body
            $Asset = $res.return.funds.($AssetName)
        }
資産情報はInvoke-RestMethodコマンドのPostメソッドで取得できます。このときHeader情報に認証データを含めることになります。
238-242: Nonceとメソッド「get-info」をクエリー値としてGet-Headerコマンドに渡しHeader情報を取得します。243-244: Invoke-RestMethodコマンドの実行結果から、BTCもしくはJPYの値を抽出します。
Get-Asset "FISCO" "jpy"10,000
売買注文:Set-Order
FISCOではWeb取引画面やAPIでも現物の指値注文のみ可能となっています。
        "FISCO"{
            $Url = "https://api.fcce.jp/tapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "trade")            
            $Body.Add("currency_pair", "btc_jpy")
            If($Side -eq "sell"){$Side = "ask"}ElseIf($Side -eq "buy"){$Side = "bid"}
            $Body.Add("action", "$Side")
            $Body.Add("price", "$Price")
            $Body.Add("amount", "$Amount")
            $Header = Get-Header $SiteName $Body
        }
156: 現物取引のエンドポイントはhttps://api.fcce.jp/tapiとなります。409: methodとしてtradeを指定します。411: 売り注文の場合は”ask”、買い注文の場合は”bid”となります。413: 注文金額が5円刻みとなっているので、金額末尾が必ず「0」もしくは「5」となるように金額を設定する必要があります。414: 最小注文単位は0.0001btcとなります。417-428: FISCOのAPIでは取引注文が集中するとAPIエラーが頻繁に発生するため、エラー処理Try~Catchで行います。419-422: 注文がエラーとなると、”success”のプロパティー値が0となるので、この場合エラーメッセージを表示して戻り値を0としています。423: 現物取引の場合、注文が成功するとOrderIDは全て0となってしまうので、注文成功したという意味で1を戻り値とします。425-428: その他エラーとなった場合もエラーメッセージを表示して戻り値を0とします。
Set-Order "FISCO" "buy" "limit" "700000" "0.001"1
注文の確認:Get-Order
        "FISCO"{
            $Url = "https://api.fcce.jp/tapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "active_orders")  
            $Header = Get-Header $SiteName $Body    
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body
            Return $res.return
        }
498: 現在有効となっている注文一覧を取得します。
注文のキャンセル:Cancel-Order
        "FISCO"{
            $Url = "https://api.fcce.jp/tapi"
            $Nonce = ([System.DateTime]::UtcNow - (Get-Date("1970, 1, 1"))).TotalSeconds
            $Body = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $Body.Add("nonce", "$Nonce")
            $Body.Add("method", "cancel_order")            
            $Body.Add("order_id", "$OrderID")
            $Header = Get-Header $SiteName $Body       
            $res = Invoke-RestMethod $Url -Method POST -Headers $Header -Body $Body 
        }
FISCO場合、注文時にオーダーIDが確認できないので、Get-Orderコマンドで取引注文IDを確認した後キャンセル時に指定します。
Cancel-Order "FISCO" 12345678
コマンドサンプル
以降は作成した各コマンドのサンプルとなります。
#取引所 (bitFlyer, bitFlyerFX, Zaif, ZaifFX, bitbank, QUOINE, QUOINEFX, BTCBOX, FISCO)
    $SiteName = "FISCO"
#資産 (jpy, btc)
    $AssetName = "jpy"
#売買 (sell, buy)
    $Side = "buy"
#指値・成行 (limit, market)
    $Type = "limit"
#価格(FISCO:5円単位)
    $Price = 600000
#注文量 (bitFlyer>0.001, Zaif>0.0001, bitbank>0.001, QUOINE>0.001, BTCBOX>0.001, FISCO>0.0001)
    $Amount = 0.001
#ビットコイン価格
    $BtcPrice = 700000
#価格情報
    #Get-Price $SiteName
#資産情報
    #Get-Asset $SiteName $AssetName
#売買注文
    #$OrderID = Set-Order $SiteName $Side $Type $Price $Amount
#注文情報
    #Get-Order $SiteName
#注文取消し
    #Cancel-Order $SiteName $OrderID
#FXポジションクローズ
    #Close-FX $SiteName $OrderID $BtcPrice
以上がFISCOの基本的なAPIの利用方法となります。FISCOはZaif現物取引のAPIに仕様が非常に似ています。FX取引がなく、APIの機能としても基本的なものに限定されているので、理解はしやすいかと思います。以上、国内の主要な仮想通貨取引所のAPI利用法をご紹介してきました。次回からは、これらのAPIを利用して、どのように仮想通貨を取引を行っていくのか、具体的な取引手法を解説しながら、APIを利用してPowerShellによる仮想通貨システムトレードを解説していく予定です。参考FISCO (https://fcce.jp/)APIリファレンス(https://fcce.jp/api-docs)