Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intall everything #8

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ to run on Windows 7 machines.

## Installation
1. Clone the repository and go to its folder.
2. Compile the code using Visual Studio, MSBuild or via this handy script file:
2. Clone mdvx/CryptoExchange.Net and mdvx/Binance.Net from
* https://github.com/mdvx/CryptoExchange.Net
* https://github.com/mdvx/Binance.Net
3. Build the Release target for these two Projects, the forks include Strong name signing for Excel.

`build.cmd`
4. Compile the code using Visual Studio
Open project in Visual Studio and fix up the references to Binance.net and CryptoExchange.Net

3. Register the COM server by running the following script in admin command prompt:
5. Register the COM server by running the following script in admin command prompt:

`register.cmd`

Expand All @@ -20,11 +24,15 @@ Once the RTD server has been installed, you can use it from Excel via the RTD ma
This is the syntax:

* `=RTD("crypto",,"GDAX", instrument, field)`

* `=RTD("crypto",,"CLOCK")` // A high fidelity clock update every few milliseconds

* `=RTD("crypto",,"BINANCE", instrument, field)`
* `=RTD("crypto",,"BINANCE_DEPTH",instrument, field,depth)` // depth is 0-9
* `=RTD("crypto",,"BINANCE_TRADE",instrument, field)`
* `=RTD("crypto",,"BINANCE_CANDLE",instrument, interval, field)` // interval is 0-11
* `=RTD("crypto",,"BINANCE_HISTORY",instrument)` // not yet working
* `=RTD("crypto",,"BINANCE_HISTORY",instrument)` // Returns and string Array
* `=RTD("crypto",,"BINANCE", "DRIFT")` // drift between Binance Server and RTD Server

*All* currency pairs traded on GDAX are supported, including the main ones:
* BTC-USD
Expand Down
Binary file modified doc/crypto.xlsx
Binary file not shown.
273 changes: 188 additions & 85 deletions src/CryptoRtd/BinanceAdapter.cs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/CryptoRtd/CryptoRtd.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{95F50618-AA6C-4551-8CD7-49E04C0D5CE9}</ProjectGuid>
<ProjectGuid>{5B968A88-46DB-429C-A10E-AE86018FBAEB}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CryptoRtd</RootNamespace>
Expand Down
154 changes: 91 additions & 63 deletions src/CryptoRtd/CryptoRtdServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,21 @@ object IRtdServer.ConnectData (int topicId,
{
case CLOCK:
_subMgr.Subscribe(topicId, CLOCK);
break;
return DateTime.Now.ToLocalTime();
}
return "Unsupported origin: " + origin;
}
else if (strings.Length == 2)
{
string origin = strings.GetValue(0).ToString().ToUpperInvariant();
string stat = strings.GetValue(1).ToString().ToUpperInvariant();

default:
return "Unsupported origin: " + origin;
switch(origin)
{
case BinanceAdapter.BINANCE:
return _binanceAdapter.QueryInfo(stat);
}
return "Unsupported origin: " + origin;
}
else if (strings.Length >= 3)
{
Expand All @@ -130,18 +140,14 @@ object IRtdServer.ConnectData (int topicId,

switch (origin)
{
case CLOCK:
_subMgr.Subscribe(topicId, CLOCK);
break;

case GDAX:
lock (_subMgr)
{
// Let's use Empty strings for now
_subMgr.Subscribe(topicId,origin,String.Empty,instrument,field);
}
Task.Run(() => SubscribeGdaxWebSocketToTicker(topicId,instrument)); // dont block excel
break;
return SubscriptionManager.UninitializedValue;

case BinanceAdapter.BINANCE:
case BinanceAdapter.BINANCE_24H:
Expand All @@ -153,18 +159,11 @@ object IRtdServer.ConnectData (int topicId,
if (strings.Length > 3)
Int32.TryParse(strings.GetValue(3).ToString(), out depth);

lock (_subMgr)
{
if (depth < 0)
_subMgr.Subscribe(topicId,origin,String.Empty,instrument,field);
else
_subMgr.Subscribe(topicId,origin,String.Empty,instrument,field,depth);
}
return _binanceAdapter.Subscribe(origin, instrument, field, depth);
return _binanceAdapter.Subscribe(topicId, origin, instrument, field, depth);
default:
return "ERROR: Unsupported origin: " + origin;
}
return SubscriptionManager.UninitializedValue;
//return SubscriptionManager.UninitializedValue;
}
return "ERROR: Expected: origin, vendor, instrument, field, [depth]";
}
Expand Down Expand Up @@ -257,15 +256,15 @@ private void OnWebSocketMessageReceived (object sender, WebSocketMessageEventArg

lock (_subMgr)
{
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, "BID"),jobj.best_bid);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, "ASK"),jobj.best_ask);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, "LAST_SIZE"),jobj.last_size);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, RtdFields.BID),jobj.best_bid);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, RtdFields.ASK),jobj.best_ask);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, RtdFields.LAST_SIZE),jobj.last_size);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, "LAST_PRICE"),jobj.price);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, "LAST_SIDE"),jobj.side);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, "high_24h"), jobj.high_24h);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, "low_24h"), jobj.low_24h);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, "open_24h"), jobj.open_24h);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, "volume_24h"), jobj.volume_24h);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, "HIGH_24H"), jobj.high_24h);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, "LOW_24H"), jobj.low_24h);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, "OPEN_24H"), jobj.open_24h);
_subMgr.Set(SubscriptionManager.FormatPath(origin, String.Empty, prod, "VOLUME_24H"), jobj.volume_24h);
}
}
}
Expand Down Expand Up @@ -328,14 +327,21 @@ class SubscriptionManager

readonly Dictionary<string, SubInfo> _subByPath;
readonly Dictionary<int, SubInfo> _subByTopicId;
readonly Dictionary<int, SubInfo> _dirtyMap;

public SubscriptionManager ()
{
_subByPath = new Dictionary<string, SubInfo>();
_subByTopicId = new Dictionary<int, SubInfo>();
_dirtyMap = new Dictionary<int, SubInfo>();
}

public bool IsDirty { get; private set; }
public bool IsDirty {
get
{
return _dirtyMap.Count > 0;
}
}
public void Subscribe(int topicId, string origin)
{
var subInfo = new SubInfo(topicId, origin);
Expand All @@ -345,24 +351,26 @@ public void Subscribe(int topicId, string origin)
}
public void Subscribe(int topicId, string origin, string vendor, string instrument, string field)
{
var subInfo = new SubInfo(
topicId,
FormatPath(origin, vendor, instrument, field));
var path = FormatPath(origin, vendor, instrument, field);
if (_subByPath.TryGetValue(path, out SubInfo subInfo))
subInfo.TopicId = topicId;
else
subInfo = new SubInfo(topicId, path);

_subByTopicId[topicId] = subInfo;
_subByPath[subInfo.Path] = subInfo;
}
public void Subscribe(int topicId, string origin, string vendor, string instrument, string field, int depth)
{
var subInfo = new SubInfo(
topicId,
FormatPath(origin, vendor, instrument, field, depth));
var path = FormatPath(origin, vendor, instrument, field, depth);
if (!_subByTopicId.TryGetValue(topicId, out SubInfo subInfo))
subInfo = new SubInfo(topicId, path);

_subByTopicId[topicId] = subInfo;
_subByPath[subInfo.Path] = subInfo;
}

public void Unsubscribe (int topicId)
public void Unsubscribe(int topicId)
{
SubInfo subInfo;
if (_subByTopicId.TryGetValue(topicId, out subInfo))
Expand All @@ -376,18 +384,15 @@ public List<UpdatedValue> GetUpdatedValues ()
{
var updated = new List<UpdatedValue>(_subByTopicId.Count);

// For simplicity, let's just do a linear scan
foreach (var subInfo in _subByTopicId.Values)
lock (_dirtyMap)
{
if (subInfo.IsDirty)
foreach (var subInfo in _dirtyMap.Values)
{
updated.Add(new UpdatedValue(subInfo.TopicId, subInfo.Value));
subInfo.IsDirty = false;
}
_dirtyMap.Clear();
}

IsDirty = false;

return updated;
}

Expand All @@ -399,7 +404,8 @@ public void Set(string path, object value)
if (value != subInfo.Value)
{
subInfo.Value = value;
IsDirty = true;
lock (_dirtyMap)
_dirtyMap[subInfo.TopicId] = subInfo;
}
}
}
Expand All @@ -411,53 +417,75 @@ public void Set(int topicId, object value)
if (value != subInfo.Value)
{
subInfo.Value = value;
IsDirty = true;
lock(_dirtyMap)
_dirtyMap[subInfo.TopicId] = subInfo;
}
}
}

public void PreSet(string path, object value)
{
SubInfo subInfo;
if (_subByPath.TryGetValue(path, out subInfo))
{
if (value != subInfo.Value)
{
subInfo.Value = value;
lock (_dirtyMap)
_dirtyMap[subInfo.TopicId] = subInfo;
}
}
else
{
_subByPath[path] = new SubInfo(path, value);
}
}
public static string FormatPath(string origin, string vendor, string instrument, string field)
{
return string.Format("{0}/{1}/{2}/{3}",
origin.ToUpperInvariant(),
vendor.ToUpperInvariant(),
instrument.ToUpperInvariant(),
field.ToUpperInvariant());
vendor?.ToUpperInvariant(),
instrument?.ToUpperInvariant(),
field?.ToUpperInvariant());
}
public static string FormatPath(string origin, string vendor, string instrument, string field, int num)
{
return string.Format("{0}/{1}/{2}/{3}/{4}",
origin.ToUpperInvariant(),
vendor.ToUpperInvariant(),
instrument.ToUpperInvariant(),
field.ToUpperInvariant(),
vendor?.ToUpperInvariant(),
instrument?.ToUpperInvariant(),
field?.ToUpperInvariant(),
num); // can be depth or limit
}
class SubInfo
{
public int TopicId { get; private set; }
public string Path { get; private set; }

private object _value;
internal object GetValue(string origin, string vendor, string instrument, string field)
{
return _subByPath[FormatPath(origin, vendor, instrument, field)].Value;
}
public object GetValue(int topicId)
{
if (_subByTopicId.TryGetValue(topicId, out SubInfo sub))
return sub.Value;

public object Value
{
get { return _value; }
set
{
_value = value;
IsDirty = true;
}
}
return UninitializedValue;
}

public bool IsDirty { get; set; }
class SubInfo
{
public int TopicId { get; set; }
public string Path { get; private set; }
public object Value { get; set; }

public SubInfo (int topicId, string path)
{
TopicId = topicId;
Path = path;
Value = UninitializedValue;
IsDirty = false;
}
public SubInfo(string path, object value)
{
TopicId = -1; // this a PreSet or PreCache
Path = path;
Value = value;
}
}
}
Expand Down
23 changes: 21 additions & 2 deletions src/CryptoRtd/RtdFields.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,28 @@ public class RtdFields
{
public const string BINANCE = "BINANCE";


//Info
public const string DRIFT = "DRIFT";
public const string EXCHANGE_TIME = "EXCHANGE_TIME";
public const string EXCHANGE_TIMEZONE = "EXCHANGE_TIMEZONE";
public const string EXCHANGE_SYMBOLS = "EXCHANGE_SYMBOLS";
//public const string EXCHANGE_RATE_LIMITS = "EXCHANGE_RATE_LIMITS";
//public const string EXCHANGE_FILTERS = "EXCHANGE_FILTERS";

public const string BASE_ASSET = "BASE_ASSET";
public const string BASE_ASSET_PRECISION = "BASE_ASSET_PRECISION";
//public const string FILTERS = "FILTERS";
public const string ICEBERG_ALLOWED = "ICEBERG_ALLOWED";
public const string NAME = "NAME";
public const string ORDER_TYPES = "ORDER_TYPES";
public const string QUOTE_ASSET ="QUOTE_ASSET";
public const string QUOTE_ASSET_PRECISION = "QUOTE_ASSET_PRECISION";
public const string STATUS = "STATUS";

// Price
public const string PRICE = "PRICE";
public const string SYMBOL = "SYMBOL";
public const string DRIFT = "DRIFT";
public static readonly string[] PRICE_FIELDS = { PRICE, SYMBOL, DRIFT };

// 24Price
Expand Down Expand Up @@ -44,11 +62,12 @@ public class RtdFields
OPEN, OPEN_TIME, CLOSE, CLOSE_TIME,VWAP, PRICE_PCT, PRICE_CHG, TRADES, SPREAD };

// Depth
public const string LAST_UPDATE_ID = "LAST_UPDATE_ID";
public const string ASK_DEPTH = "ASK_DEPTH";
public const string ASK_DEPTH_SIZE = "ASK_DEPTH_SIZE";
public const string BID_DEPTH = "BID_DEPTH";
public const string BID_DEPTH_SIZE = "BID_DEPTH_SIZE";
public static readonly string[] DEPTH = { SYMBOL, ASK_DEPTH, ASK_DEPTH_SIZE, BID_DEPTH, BID_DEPTH_SIZE };
public static readonly string[] DEPTH = { SYMBOL, LAST_UPDATE_ID, ASK_DEPTH, ASK_DEPTH_SIZE, BID_DEPTH, BID_DEPTH_SIZE };


// Trade
Expand Down
1 change: 0 additions & 1 deletion src/crypto-rtd.sln
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
..\build.cmd = ..\build.cmd
..\doc\crypto-rtd-excel.png = ..\doc\crypto-rtd-excel.png
..\doc\crypto.xlsx = ..\doc\crypto.xlsx
..\doc\crypto2.xlsx = ..\doc\crypto2.xlsx
..\LICENSE = ..\LICENSE
..\README.md = ..\README.md
..\register.cmd = ..\register.cmd
Expand Down