316 lines
7.9 KiB
C#
316 lines
7.9 KiB
C#
using System;
|
|
using sharp.extensions;
|
|
using System.Collections.Generic;
|
|
using sharp.json;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Globalization;
|
|
using sharp.trading.cache;
|
|
|
|
/**
|
|
* https://bittrex.com/Api/v2.0/pub/market/GetTicks?marketName=BTC-WAVES&tickInterval=day
|
|
*
|
|
**/
|
|
|
|
namespace sharp.trading.bittrex
|
|
{
|
|
public class BittrexConnector : TradingConnection
|
|
{
|
|
HMACSHA512 hmac;
|
|
ASCIIEncoding ascii = new ASCIIEncoding();
|
|
|
|
public string ApiKey { get; set; }
|
|
public string ApiSecret { get; set; }
|
|
|
|
JSONMarket[] activeMarkets;
|
|
|
|
BittrexCurrency[] currentCurrencies;
|
|
|
|
public List<BittrexOrder> orderCache = new List<BittrexOrder>();
|
|
|
|
UInt64 nextNonce = 1;
|
|
|
|
public BittrexConnector()
|
|
:base("bittrex.com")
|
|
{
|
|
ApiKey = "";
|
|
ApiSecret = "";
|
|
initialize();
|
|
}
|
|
|
|
public BittrexConnector(string apiKey,string apiSecret)
|
|
:base("bittrex.com")
|
|
{
|
|
ApiKey = apiKey;
|
|
ApiSecret = apiSecret;
|
|
|
|
hmac = new HMACSHA512(Encoding.UTF8.GetBytes(apiSecret));
|
|
|
|
initialize();
|
|
}
|
|
|
|
public override object[] getConstructorArguments()
|
|
{
|
|
return new object[]{
|
|
ApiKey,
|
|
ApiSecret
|
|
};
|
|
}
|
|
|
|
private void initialize(){
|
|
currentCurrencies = _getCurrencies();
|
|
fetchActiveMarkets();
|
|
nextNonce = (ulong)DateTime.UtcNow.Ticks;
|
|
nextNonce *= 100;
|
|
}
|
|
|
|
private JSONMarket jsonMarket(string marketSymbol,string baseSymbol){
|
|
foreach (JSONMarket jm in activeMarkets){
|
|
if (jm.MarketCurrency.Equals(marketSymbol) && jm.BaseCurrency.Equals(baseSymbol)){
|
|
return jm;
|
|
}
|
|
}
|
|
throw new IndexOutOfRangeException(String.Format("Market {0}-{1} not found.",marketSymbol,baseSymbol));
|
|
}
|
|
|
|
private void fetchActiveMarkets(){
|
|
List<JSONMarket> markets = new List<JSONMarket>();
|
|
|
|
JSON reply = JSONWebRequest.Call("https://bittrex.com/api/v1.1/public/getmarkets");
|
|
|
|
if (reply["success"].Boolean == true){
|
|
|
|
foreach (JSON market in reply["result"]){
|
|
JSONMarket jm = market.To<JSONMarket>();
|
|
if (jm.IsActive){
|
|
markets.Add(jm);
|
|
}
|
|
}
|
|
|
|
activeMarkets = markets.ToArray();
|
|
} else {
|
|
throw new Exception("Failed to read active markets.");
|
|
}
|
|
}
|
|
|
|
public override Pair<string>[] listMarkets()
|
|
{
|
|
Pair<string>[] pairs = new Pair<string>[activeMarkets.Length];
|
|
|
|
for (int n = 0; n < pairs.Length;n++){
|
|
pairs[n] = new Pair<string>(activeMarkets[n].BaseCurrency,activeMarkets[n].MarketCurrency);
|
|
}
|
|
|
|
return pairs;
|
|
}
|
|
|
|
public override Market openMarket(string tradedSymbol, string payingSymbol)
|
|
{
|
|
JSONMarket jm = jsonMarket(tradedSymbol, payingSymbol);
|
|
BittrexMarket m = new BittrexMarket(this, jm);
|
|
return m;
|
|
}
|
|
|
|
public override Balance[] getBalances()
|
|
{
|
|
JSON balances = authenticatedCall("https://bittrex.com/api/v1.1/account/getbalances");
|
|
return balances.To<BittrexBalance[]>();
|
|
}
|
|
|
|
public override Order[] getOpenOrders()
|
|
{
|
|
JSON openorders = authenticatedCall("https://bittrex.com/api/v1.1/market/getopenorders");
|
|
return openorders.To<BittrexOrder[]>();
|
|
}
|
|
|
|
public override Order[] getHistoricOrders()
|
|
{
|
|
JSON historicorders = authenticatedCall("https://bittrex.com/api/v1.1/account/getorderhistory");
|
|
Order[] orders = historicorders.To<BittrexOrder[]>();
|
|
foreach (Order o in orders){
|
|
o.IsOpen = false;
|
|
}
|
|
return orders;
|
|
}
|
|
|
|
public void UpdateOrderCache(){
|
|
orderCache.Clear();
|
|
//orderCache.AddRange((BittrexOrder[])getOpenOrders());
|
|
//orderCache.AddRange((BittrexOrder[])getHistoricOrders());
|
|
}
|
|
|
|
public BittrexOrder findCachedOrder(string orderID){
|
|
foreach (BittrexOrder o in orderCache){
|
|
if (o.OrderID.Equals(orderID)){
|
|
return o;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public override Order createOrder(OrderType orderType, OrderTarget orderTarget, string marketSymbol, string baseSymbol, double amount, double price)
|
|
{
|
|
if (orderTarget != OrderTarget.LIMIT){
|
|
throw new NotImplementedException("Only Limit orders are currently supported");
|
|
}
|
|
|
|
string marketname = string.Format("{0}-{1}", baseSymbol, marketSymbol);
|
|
KeyValuePair<string, string>[] parms = new KeyValuePair<string, string>[]{
|
|
new KeyValuePair<string, string>("market",marketname),
|
|
new KeyValuePair<string, string>("quantity",amount.ToString(CultureInfo.InvariantCulture)),
|
|
new KeyValuePair<string, string>("rate",price.ToString(CultureInfo.InvariantCulture))
|
|
};
|
|
|
|
string url = null;
|
|
|
|
switch (orderType)
|
|
{
|
|
case OrderType.BUY:
|
|
url = "https://bittrex.com/api/v1.1/market/buylimit";
|
|
break;
|
|
case OrderType.SELL:
|
|
url = "https://bittrex.com/api/v1.1/market/selllimit";
|
|
break;
|
|
}
|
|
|
|
JSON result = authenticatedCall(url, parms);
|
|
|
|
return getOrder(result["uuid"].String);
|
|
}
|
|
|
|
public override Currency[] getCurrencies()
|
|
{
|
|
return this.currentCurrencies.Segment(0);
|
|
}
|
|
|
|
private BittrexOrder getOrderOnline(string orderID)
|
|
{
|
|
KeyValuePair<string, string>[] parms = new KeyValuePair<string, string>[]{
|
|
new KeyValuePair<string, string>("uuid",orderID)
|
|
};
|
|
|
|
JSON json = authenticatedCall("https://bittrex.com/api/v1.1/account/getorder", parms);
|
|
return json.To<BittrexOrder>();
|
|
}
|
|
|
|
public override Order getOrder(string orderID)
|
|
{
|
|
if (orderCache.Count == 0){
|
|
UpdateOrderCache();
|
|
}
|
|
|
|
BittrexOrder cached = findCachedOrder(orderID);
|
|
if (cached != null){
|
|
return cached;
|
|
}
|
|
|
|
return getOrderOnline(orderID);
|
|
}
|
|
|
|
public override void refreshOrder(Order order){
|
|
BittrexOrder refresh = getOrderOnline(order.OrderID);
|
|
order.FilledVolume = refresh.FilledVolume;
|
|
order.PayedFees = refresh.PayedFees;
|
|
order.PayedPrice = refresh.PayedPrice;
|
|
order.OrderState = refresh.OrderState;
|
|
order.IsOpen = refresh.IsOpen;
|
|
}
|
|
|
|
public override void cancelOrder(string orderID)
|
|
{
|
|
KeyValuePair<string, string>[] parms = new KeyValuePair<string, string>[]{
|
|
new KeyValuePair<string, string>("uuid",orderID)
|
|
};
|
|
|
|
authenticatedCall("https://bittrex.com/api/v1.1/market/cancel", parms);
|
|
}
|
|
|
|
|
|
private BittrexCurrency[] _getCurrencies()
|
|
{
|
|
JSON jcurrencies = publicCall("https://bittrex.com/api/v1.1/public/getcurrencies");
|
|
return jcurrencies.To<BittrexCurrency[]>();
|
|
}
|
|
|
|
|
|
public JSON publicCall(string url){
|
|
KeyValuePair<string, string>[] parms = new KeyValuePair<string, string>[0];
|
|
return publicCall(url, parms);
|
|
}
|
|
public JSON publicCall(string url, KeyValuePair<string, string>[] parms)
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
sb.Append(url);
|
|
sb.Append("?");
|
|
|
|
foreach (KeyValuePair<string, string> parm in parms)
|
|
{
|
|
sb.Append(Uri.EscapeDataString(parm.Key));
|
|
sb.Append("=");
|
|
sb.Append(Uri.EscapeDataString(parm.Value));
|
|
sb.Append("&");
|
|
}
|
|
|
|
string finalurl = sb.ToString();
|
|
finalurl = finalurl.Substring(0, finalurl.Length-1);
|
|
|
|
|
|
JSON result = JSONWebRequest.Call(finalurl);
|
|
|
|
if (!result["success"].Boolean)
|
|
{
|
|
throw new BittrexException(result);
|
|
}
|
|
|
|
return result["result"];
|
|
}
|
|
|
|
|
|
private JSON authenticatedCall(string url)
|
|
{
|
|
KeyValuePair<string, string>[] parms = new KeyValuePair<string, string>[0];
|
|
return authenticatedCall(url, parms);
|
|
}
|
|
|
|
private JSON authenticatedCall(string url, KeyValuePair<string, string>[] parms)
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
sb.Append(url);
|
|
sb.Append("?");
|
|
|
|
foreach (KeyValuePair<string, string> parm in parms)
|
|
{
|
|
sb.Append(Uri.EscapeDataString(parm.Key));
|
|
sb.Append("=");
|
|
sb.Append(Uri.EscapeDataString(parm.Value));
|
|
sb.Append("&");
|
|
}
|
|
sb.Append("apikey=");
|
|
sb.Append(ApiKey);
|
|
sb.Append("&nonce=");
|
|
sb.Append(nextNonce++);
|
|
|
|
string finalurl = sb.ToString();
|
|
string apisign = HexString.toString(hmac.ComputeHash(Encoding.ASCII.GetBytes(finalurl))).ToLower();
|
|
|
|
Dictionary<string, string> headers = new Dictionary<string, string>();
|
|
headers["apisign"] = apisign;
|
|
|
|
//Console.WriteLine("CALL: {0}", finalurl);
|
|
|
|
JSON result = JSONWebRequest.Call(finalurl, null, headers);
|
|
|
|
if (!result["success"].Boolean)
|
|
{
|
|
throw new BittrexException(result);
|
|
}
|
|
|
|
return result["result"];
|
|
}
|
|
|
|
}
|
|
}
|