Compare commits

...

14 Commits
13402 ... 11527

Author SHA1 Message Date
Jasper van Merle
b5cebaefa6 Replace ICSharp with .NET Interactive 2021-04-23 19:07:26 -03:00
Martin-Molinero
15b9567cdf Fixes
- Revert dockerfile FROM custom changes
- Adjust and fix regression algorithms
   - Option assignment will be deterministic in the order
   - 'Rolling Averaged Population' is calculated using doubles, updating
     expected values.
- Update readme, removing references to mono
- Add missing Py.Gil lock
2021-04-23 17:58:11 -03:00
Jasper van Merle
4e1f083dff Add vsdbg to Dockerfile 2021-04-23 16:15:13 -03:00
Jasper van Merle
95d72947a7 Update comment 2021-04-23 16:15:13 -03:00
Jasper van Merle
b18a290de5 Research fixes 2021-04-23 16:15:13 -03:00
Jasper van Merle
767236d59e Fix ConsoleLeanOptimizer 2021-04-23 16:15:13 -03:00
Jasper van Merle
325867cce6 Don't call ReadKey when input is redirected 2021-04-23 16:15:13 -03:00
Martin-Molinero
5cee1e6e31 Fixes
- Travis will use dotnet, not nunit nor mono
- Remove mono from foundation image
- Fix python setup in research
- Fix unit tests
2021-04-23 16:15:13 -03:00
Martin-Molinero
319b813278 Remove unrequired references 2021-04-23 16:15:13 -03:00
Martin-Molinero
7f0ad73485 Fix rebase
- Fix ambiguous Index
- Remove StrategyCapacity.cs
- Update System.Threading.Tasks.Extensionsy
2021-04-23 16:15:13 -03:00
Stefano Raggi
7994d0ee49 Upgrade IBAutomater to v1.0.51
ignored, and an empty message aborts the commit.
2021-04-23 16:15:13 -03:00
Martin Molinero
4b68db0fbe Remove FXCM 2021-04-23 16:15:13 -03:00
Martin Molinero
59fbc06c1a Fix ambiguous errors. Add IBAutomator net5 2021-04-23 16:15:13 -03:00
Gerardo Salazar
b4506dcc51 Update projects to use .NET 5.0, the successor to .NET Core 2021-04-23 16:15:13 -03:00
86 changed files with 222 additions and 4495 deletions

View File

@@ -8,22 +8,17 @@ on:
jobs:
build:
runs-on: ubuntu-16.04
runs-on: ubuntu-20.04
container:
image: quantconnect/lean:foundation
steps:
- uses: actions/checkout@v2
- name: Restore nuget dependencies
run: |
nuget restore QuantConnect.Lean.sln -v quiet
nuget install NUnit.Runners -Version 3.11.1 -OutputDirectory testrunner
- name: Build
run: msbuild /p:Configuration=Release /p:VbcToolExe=vbnc.exe /v:quiet /p:WarningLevel=1 QuantConnect.Lean.sln
run: dotnet build /p:Configuration=Release /v:quiet /p:WarningLevel=1 QuantConnect.Lean.sln
- name: Run Tests
run: mono ./testrunner/NUnit.ConsoleRunner.3.11.1/tools/nunit3-console.exe ./Tests/bin/Release/QuantConnect.Tests.dll --where "cat != TravisExclude" --labels=Off --params:log-handler=ConsoleErrorLogHandler
run: dotnet test ./Tests/bin/Release/QuantConnect.Tests.dll --filter TestCategory!=TravisExclude -- TestRunParameters.Parameter\(name=\"log-handler\", value=\"ConsoleErrorLogHandler\"\)
- name: Generate & Publish python stubs
run: |

View File

@@ -1,9 +1,8 @@
sudo: required
language: csharp
mono: none
dotnet: 5.0
mono:
- 5.12.0
solution: QuantConnect.Lean.sln
os: linux
dist: focal
before_install:
- export PATH="$HOME/miniconda3/bin:$PATH"
- export PYTHONNET_PYDLL="$HOME/miniconda3/lib/libpython3.6m.so"
@@ -18,11 +17,9 @@ before_install:
- conda install -y cython=0.29.15
- conda install -y scipy=1.4.1
- conda install -y wrapt=1.12.1
install:
- nuget install NUnit.Runners -Version 3.11.1 -OutputDirectory testrunner
script:
- dotnet nuget add source $TRAVIS_BUILD_DIR/LocalPackages
- dotnet build /p:Configuration=Release /p:VbcToolExe=vbnc.exe /v:quiet /p:WarningLevel=1 QuantConnect.Lean.sln
- mono ./testrunner/NUnit.ConsoleRunner.3.11.1/tools/nunit3-console.exe ./Tests/bin/Release/QuantConnect.Tests.dll --where "cat != TravisExclude" --labels=Off --params:log-handler=ConsoleErrorLogHandler
- dotnet build /p:Configuration=Release /v:quiet /p:WarningLevel=1 QuantConnect.Lean.sln
- dotnet test ./Tests/bin/Release/QuantConnect.Tests.dll --filter TestCategory!=TravisExclude -- TestRunParameters.Parameter\(name=\"log-handler\", value=\"ConsoleErrorLogHandler\"\)
- chmod +x ci_build_stubs.sh
- sudo -E ./ci_build_stubs.sh -d -t -g -p

View File

@@ -79,19 +79,19 @@ namespace QuantConnect.Algorithm.CSharp
// things like manually added, auto added, internal, and any other boolean state we need to track against a single security)
throw new Exception("The underlying equity data should NEVER be removed in this algorithm because it was manually added");
}
if (_expectedSecurities.AreDifferent(LinqExtensions.ToHashSet(Securities.Keys)))
if (_expectedSecurities.AreDifferent(Securities.Keys.ToHashSet()))
{
var expected = string.Join(Environment.NewLine, _expectedSecurities.OrderBy(s => s.ToString()));
var actual = string.Join(Environment.NewLine, Securities.Keys.OrderBy(s => s.ToString()));
throw new Exception($"{Time}:: Detected differences in expected and actual securities{Environment.NewLine}Expected:{Environment.NewLine}{expected}{Environment.NewLine}Actual:{Environment.NewLine}{actual}");
}
if (_expectedUniverses.AreDifferent(LinqExtensions.ToHashSet(UniverseManager.Keys)))
if (_expectedUniverses.AreDifferent(UniverseManager.Keys.ToHashSet()))
{
var expected = string.Join(Environment.NewLine, _expectedUniverses.OrderBy(s => s.ToString()));
var actual = string.Join(Environment.NewLine, UniverseManager.Keys.OrderBy(s => s.ToString()));
throw new Exception($"{Time}:: Detected differences in expected and actual universes{Environment.NewLine}Expected:{Environment.NewLine}{expected}{Environment.NewLine}Actual:{Environment.NewLine}{actual}");
}
if (_expectedData.AreDifferent(LinqExtensions.ToHashSet(data.Keys)))
if (_expectedData.AreDifferent(data.Keys.ToHashSet()))
{
var expected = string.Join(Environment.NewLine, _expectedData.OrderBy(s => s.ToString()));
var actual = string.Join(Environment.NewLine, data.Keys.OrderBy(s => s.ToString()));
@@ -183,7 +183,7 @@ namespace QuantConnect.Algorithm.CSharp
if (changes.RemovedSecurities
.Where(x => x.Symbol.SecurityType == SecurityType.Option)
.ToHashSet(s => s.Symbol)
.AreDifferent(LinqExtensions.ToHashSet(_expectedContracts)))
.AreDifferent(_expectedContracts.ToHashSet()))
{
throw new Exception("Expected removed securities to equal expected contracts added");
}

View File

@@ -151,18 +151,18 @@ namespace QuantConnect.Algorithm.CSharp
{"Drawdown", "0.500%"},
{"Expectancy", "1.393"},
{"Net Profit", "32.840%"},
{"Sharpe Ratio", "7.14272222483913E+15"},
{"Sharpe Ratio", "7142722224839133"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "83%"},
{"Win Rate", "17%"},
{"Profit-Loss Ratio", "13.36"},
{"Alpha", "2.59468989671647E+16"},
{"Alpha", "25946898967164744"},
{"Beta", "66.241"},
{"Annual Standard Deviation", "3.633"},
{"Annual Variance", "13.196"},
{"Information Ratio", "7.25220453625048E+15"},
{"Information Ratio", "7252204536250480"},
{"Tracking Error", "3.578"},
{"Treynor Ratio", "391705233723350"},
{"Treynor Ratio", "391705233723349.5"},
{"Total Fees", "$13.00"},
{"Estimated Strategy Capacity", "$3000000.00"},
{"Fitness Score", "0.232"},

View File

@@ -117,8 +117,8 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Estimated Insight Value", "$54597.4919"},
{"Mean Population Direction", "43.4499%"},
{"Mean Population Magnitude", "43.4499%"},
{"Rolling Averaged Population Direction", "48.7105%"},
{"Rolling Averaged Population Magnitude", "48.7105%"},
{"Rolling Averaged Population Direction", "48.5717%"},
{"Rolling Averaged Population Magnitude", "48.5717%"},
{"OrderListHash", "03cc0ad5b1c4b7803b2e9483da1d7543"}
};
}

View File

@@ -136,7 +136,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Estimated Insight Value", "$1443.9623"},
{"Mean Population Direction", "62.5%"},
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "73.0394%"},
{"Rolling Averaged Population Direction", "73.1163%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "79a973155c0106b60249931daa89c54b"}
};

View File

@@ -117,7 +117,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "c5a34ac5f125ba7137b010820360f5bd"}
{"OrderListHash", "58557574cf0489dd38fb37768f509ca1"}
};
}
}

View File

@@ -4,7 +4,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<RootNamespace>QuantConnect.Algorithm.CSharp</RootNamespace>
<AssemblyName>QuantConnect.Algorithm.CSharp</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>6</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<OutputPath>bin\$(Configuration)\</OutputPath>
@@ -63,10 +63,6 @@
<PackageReference Include="NodaTime" Version="3.0.5" />
<PackageReference Include="R.NET" Version="1.9.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -138,8 +138,8 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Estimated Insight Value", "$-5454.712"},
{"Mean Population Direction", "30%"},
{"Mean Population Magnitude", "30%"},
{"Rolling Averaged Population Direction", "42.9591%"},
{"Rolling Averaged Population Magnitude", "42.9591%"},
{"Rolling Averaged Population Direction", "42.9939%"},
{"Rolling Averaged Population Magnitude", "42.9939%"},
{"OrderListHash", "1480e456536d63f95d6b06bfaffe7eaa"}
};
}

View File

@@ -4,7 +4,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<RootNamespace>QuantConnect.Algorithm.Framework</RootNamespace>
<AssemblyName>QuantConnect.Algorithm.Framework</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<OutputPath>bin\$(Configuration)\</OutputPath>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
@@ -54,10 +54,6 @@
</PackageReference>
<PackageReference Include="NodaTime" Version="3.0.5" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -4,7 +4,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<RootNamespace>QuantConnect.Algorithm.Python</RootNamespace>
<AssemblyName>QuantConnect.Algorithm.Python</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>6</LangVersion>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<OutputPath>bin\$(Configuration)\</OutputPath>

View File

@@ -45,8 +45,8 @@ using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Risk;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Algorithm.Selection;
using QuantConnect.Securities.Index;
using QuantConnect.Storage;
using Index = QuantConnect.Securities.Index.Index;
namespace QuantConnect.Algorithm
{

View File

@@ -5,7 +5,7 @@
<ProductVersion>8.0.30703</ProductVersion>
<RootNamespace>QuantConnect.Algorithm</RootNamespace>
<AssemblyName>QuantConnect.Algorithm</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<LangVersion>6</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
@@ -64,10 +64,6 @@
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NodaTime" Version="3.0.5" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />

View File

@@ -4,7 +4,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<RootNamespace>QuantConnect.AlgorithmFactory</RootNamespace>
<AssemblyName>QuantConnect.AlgorithmFactory</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>6</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<OutputPath>bin\$(Configuration)\</OutputPath>
@@ -48,10 +48,6 @@
</PackageReference>
<PackageReference Include="NodaTime" Version="3.0.5" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -5,7 +5,7 @@
<ProductVersion>8.0.30703</ProductVersion>
<RootNamespace>QuantConnect.Api</RootNamespace>
<AssemblyName>QuantConnect.Api</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<LangVersion>6</LangVersion>
@@ -58,12 +58,6 @@
<PackageReference Include="RestSharp" Version="106.6.10" />
<PackageReference Include="SharpZipLib" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Configuration" />
<Reference Include="System.Web" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -136,7 +136,7 @@ namespace QuantConnect.Brokerages.Backtesting
return result;
};
algorithm.Securities
foreach(var pair in algorithm.Securities
// we take only options that expire soon
.Where(x => x.Key.ID.SecurityType.IsOption() &&
((x.Key.ID.OptionStyle == OptionStyle.American && x.Key.ID.Date - algorithm.UtcTime <= _priorExpiration) ||
@@ -150,9 +150,11 @@ namespace QuantConnect.Brokerages.Backtesting
(OptionHolding)x.Value.Holdings,
algorithm.Securities[x.Value.Symbol.Underlying],
algorithm.Portfolio.CashBook) > 0.0m)
.ToList()
.OrderBy(pair => pair.Key))
{
// we exercise options with positive expected P/L (over basic sale of option)
.ForEach(x => backtestingBrokerage.ActivateOptionAssignment((Option)x.Value, (int)((OptionHolding)x.Value.Holdings).AbsoluteQuantity));
backtestingBrokerage.ActivateOptionAssignment((Option) pair.Value, (int) ((OptionHolding) pair.Value.Holdings).AbsoluteQuantity);
}
}

View File

@@ -1,148 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using com.fxcm.fix;
using com.fxcm.fix.pretrade;
using NodaTime;
using QuantConnect.Data;
using QuantConnect.Logging;
using QuantConnect.Packets;
using QuantConnect.Securities;
namespace QuantConnect.Brokerages.Fxcm
{
/// <summary>
/// FXCM brokerage - implementation of IDataQueueHandler interface
/// </summary>
public partial class FxcmBrokerage
{
#region IDataQueueHandler implementation
/// <summary>
/// Sets the job we're subscribing for
/// </summary>
/// <param name="job">Job we're subscribing for</param>
public void SetJob(LiveNodePacket job)
{
}
/// <summary>
/// Subscribe to the specified configuration
/// </summary>
/// <param name="dataConfig">defines the parameters to subscribe to a data feed</param>
/// <param name="newDataAvailableHandler">handler to be fired on new data available</param>
/// <returns>The new enumerator for this subscription request</returns>
public IEnumerator<BaseData> Subscribe(SubscriptionDataConfig dataConfig, EventHandler newDataAvailableHandler)
{
if (!CanSubscribe(dataConfig.Symbol))
{
return Enumerable.Empty<BaseData>().GetEnumerator();
}
var enumerator = _aggregator.Add(dataConfig, newDataAvailableHandler);
_subscriptionManager.Subscribe(dataConfig);
return enumerator;
}
/// <summary>
/// Adds the specified symbols to the subscription
/// </summary>
/// <param name="symbols">The symbols to be added keyed by SecurityType</param>
private bool Subscribe(IEnumerable<Symbol> symbols)
{
var request = new MarketDataRequest();
foreach (var symbol in symbols)
{
TradingSecurity fxcmSecurity;
if (_fxcmInstruments.TryGetValue(_symbolMapper.GetBrokerageSymbol(symbol), out fxcmSecurity))
{
request.addRelatedSymbol(fxcmSecurity);
// cache exchange time zone for symbol
DateTimeZone exchangeTimeZone;
if (!_symbolExchangeTimeZones.TryGetValue(symbol, out exchangeTimeZone))
{
exchangeTimeZone = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.FXCM, symbol, symbol.SecurityType).TimeZone;
_symbolExchangeTimeZones.Add(symbol, exchangeTimeZone);
}
}
}
request.setSubscriptionRequestType(SubscriptionRequestTypeFactory.SUBSCRIBE);
request.setMDEntryTypeSet(MarketDataRequest.MDENTRYTYPESET_ALL);
lock (_locker)
{
_gateway.sendMessage(request);
}
return true;
}
/// <summary>
/// Removes the specified configuration
/// </summary>
/// <param name="dataConfig">Subscription config to be removed</param>
public void Unsubscribe(SubscriptionDataConfig dataConfig)
{
_subscriptionManager.Unsubscribe(dataConfig);
_aggregator.Remove(dataConfig);
}
/// <summary>
/// Removes the specified symbols to the subscription
/// </summary>
/// <param name="symbols">The symbols to be removed keyed by SecurityType</param>
private bool Unsubscribe(IEnumerable<Symbol> symbols)
{
Log.Trace("FxcmBrokerage.Unsubscribe(): {0}", string.Join(",", symbols));
var request = new MarketDataRequest();
foreach (var symbol in symbols)
{
request.addRelatedSymbol(_fxcmInstruments[_symbolMapper.GetBrokerageSymbol(symbol)]);
}
request.setSubscriptionRequestType(SubscriptionRequestTypeFactory.UNSUBSCRIBE);
request.setMDEntryTypeSet(MarketDataRequest.MDENTRYTYPESET_ALL);
lock (_locker)
{
_gateway.sendMessage(request);
}
return true;
}
/// <summary>
/// Returns true if this brokerage supports the specified symbol
/// </summary>
private static bool CanSubscribe(Symbol symbol)
{
// ignore unsupported security types
if (symbol.ID.SecurityType != SecurityType.Forex && symbol.ID.SecurityType != SecurityType.Cfd)
return false;
// ignore universe symbols
return !symbol.Value.Contains("-UNIVERSE-");
}
#endregion
}
}

View File

@@ -1,548 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using com.fxcm.external.api.transport;
using com.fxcm.fix;
using com.fxcm.fix.admin;
using com.fxcm.fix.other;
using com.fxcm.fix.posttrade;
using com.fxcm.fix.pretrade;
using com.fxcm.fix.trade;
using com.fxcm.messaging;
using NodaTime;
using QuantConnect.Data.Market;
using QuantConnect.Logging;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
namespace QuantConnect.Brokerages.Fxcm
{
/// <summary>
/// FXCM brokerage - Java API related functions and interface implementations
/// </summary>
public partial class FxcmBrokerage
{
private IGateway _gateway;
private readonly object _locker = new object();
private string _currentRequest;
private const int ResponseTimeout = 5000;
private bool _isOrderUpdateOrCancelRejected;
private bool _isOrderSubmitRejected;
private readonly Dictionary<string, TradingSecurity> _fxcmInstruments = new Dictionary<string, TradingSecurity>();
private readonly Dictionary<string, CollateralReport> _accounts = new Dictionary<string, CollateralReport>();
private readonly Dictionary<string, MarketDataSnapshot> _rates = new Dictionary<string, MarketDataSnapshot>();
private readonly Dictionary<string, ExecutionReport> _openOrders = new Dictionary<string, ExecutionReport>();
// Map key: fxcmPositionId (can have multiple positions for the same symbol)
private readonly Dictionary<string, PositionReport> _openPositions = new Dictionary<string, PositionReport>();
private readonly Dictionary<string, Order> _mapRequestsToOrders = new Dictionary<string, Order>();
private readonly Dictionary<string, Order> _mapFxcmOrderIdsToOrders = new Dictionary<string, Order>();
private readonly Dictionary<string, AutoResetEvent> _mapRequestsToAutoResetEvents = new Dictionary<string, AutoResetEvent>();
private readonly HashSet<string> _pendingHistoryRequests = new HashSet<string>();
private void LoadInstruments()
{
// Note: requestTradingSessionStatus() MUST be called just after login
AutoResetEvent autoResetEvent;
lock (_locker)
{
_currentRequest = _gateway.requestTradingSessionStatus();
autoResetEvent = new AutoResetEvent(false);
_mapRequestsToAutoResetEvents[_currentRequest] = autoResetEvent;
}
if (!autoResetEvent.WaitOne(ResponseTimeout))
throw new TimeoutException("FxcmBrokerage.LoadInstruments(): Operation took longer than " +
$"{((decimal)ResponseTimeout / 1000).ToStringInvariant()} seconds."
);
}
private void LoadAccounts()
{
AutoResetEvent autoResetEvent;
lock (_locker)
{
_currentRequest = _gateway.requestAccounts();
autoResetEvent = new AutoResetEvent(false);
_mapRequestsToAutoResetEvents[_currentRequest] = autoResetEvent;
}
if (!autoResetEvent.WaitOne(ResponseTimeout))
throw new TimeoutException("FxcmBrokerage.LoadAccounts(): Operation took longer than " +
$"{((decimal)ResponseTimeout / 1000).ToStringInvariant()} seconds."
);
if (!_accounts.ContainsKey(_accountId))
throw new ArgumentException("FxcmBrokerage.LoadAccounts(): The account id is invalid: " + _accountId);
// Hedging MUST be disabled on the account
if (_accounts[_accountId].getParties().getFXCMPositionMaintenance() == "Y")
{
throw new NotSupportedException("FxcmBrokerage.LoadAccounts(): The Lean engine does not support accounts with Hedging enabled. " +
"Please contact FXCM Active Trader support to disable Hedging. They can be reached at https://www.fxcm.com/markets/contact-client-support/ through their Live Chat or Phone."
);
}
}
private void LoadOpenOrders()
{
AutoResetEvent autoResetEvent;
lock (_locker)
{
_currentRequest = _gateway.requestOpenOrders(null);
autoResetEvent = new AutoResetEvent(false);
_mapRequestsToAutoResetEvents[_currentRequest] = autoResetEvent;
}
if (!autoResetEvent.WaitOne(ResponseTimeout))
throw new TimeoutException("FxcmBrokerage.LoadOpenOrders(): Operation took longer than " +
$"{((decimal)ResponseTimeout / 1000).ToStringInvariant()} seconds."
);
}
private void LoadOpenPositions()
{
AutoResetEvent autoResetEvent;
lock (_locker)
{
_currentRequest = _terminal.Equals("Demo") ?
_gateway.requestOpenPositions(_accountId.ConvertInvariant<long>()) :
_gateway.requestOpenPositions(_accountId);
autoResetEvent = new AutoResetEvent(false);
_mapRequestsToAutoResetEvents[_currentRequest] = autoResetEvent;
}
if (!autoResetEvent.WaitOne(ResponseTimeout))
throw new TimeoutException("FxcmBrokerage.LoadOpenPositions(): Operation took longer than " +
$"{((decimal)ResponseTimeout / 1000).ToStringInvariant()} seconds."
);
}
/// <summary>
/// Provides as public access to this data without requiring consumers to reference
/// IKVM libraries
/// </summary>
public List<Tick> GetBidAndAsk(List<string> fxcmSymbols)
{
return GetQuotes(fxcmSymbols).Select(x => new Tick
{
Symbol = _symbolMapper.GetLeanSymbol(
x.getInstrument().getSymbol(),
_symbolMapper.GetBrokerageSecurityType(x.getInstrument().getSymbol()),
Market.FXCM),
BidPrice = (decimal)x.getBidClose(),
AskPrice = (decimal)x.getAskClose()
}).ToList();
}
/// <summary>
/// Gets the quotes for the symbol
/// </summary>
private List<MarketDataSnapshot> GetQuotes(List<string> fxcmSymbols)
{
// get current quotes for the instrument
var request = new MarketDataRequest();
request.setMDEntryTypeSet(MarketDataRequest.MDENTRYTYPESET_ALL);
request.setSubscriptionRequestType(SubscriptionRequestTypeFactory.SNAPSHOT);
foreach (var fxcmSymbol in fxcmSymbols)
{
request.addRelatedSymbol(_fxcmInstruments[fxcmSymbol]);
}
AutoResetEvent autoResetEvent;
lock (_locker)
{
_currentRequest = _gateway.sendMessage(request);
autoResetEvent = new AutoResetEvent(false);
_mapRequestsToAutoResetEvents[_currentRequest] = autoResetEvent;
}
if (!autoResetEvent.WaitOne(ResponseTimeout))
throw new TimeoutException("FxcmBrokerage.GetQuotes(): Operation took longer than " +
$"{((decimal)ResponseTimeout / 1000).ToStringInvariant()} seconds."
);
return _rates.Where(x => fxcmSymbols.Contains(x.Key)).Select(x => x.Value).ToList();
}
#region IGenericMessageListener implementation
/// <summary>
/// Receives generic messages from the FXCM API
/// </summary>
/// <param name="message">Generic message received</param>
public void messageArrived(ITransportable message)
{
// Dispatch message to specific handler
lock (_locker)
{
if (message is TradingSessionStatus)
OnTradingSessionStatus((TradingSessionStatus)message);
else if (message is CollateralReport)
OnCollateralReport((CollateralReport)message);
else if (message is MarketDataSnapshot)
OnMarketDataSnapshot((MarketDataSnapshot)message);
else if (message is ExecutionReport)
OnExecutionReport((ExecutionReport)message);
else if (message is RequestForPositionsAck)
OnRequestForPositionsAck((RequestForPositionsAck)message);
else if (message is PositionReport)
OnPositionReport((PositionReport)message);
else if (message is OrderCancelReject)
OnOrderCancelReject((OrderCancelReject)message);
else if (message is UserResponse || message is CollateralInquiryAck || message is Logout ||
message is MarketDataRequestReject || message is BusinessMessageReject || message is SecurityStatus)
{
// Unused messages, no handler needed
}
else
{
// Should never get here, if it does log and ignore message
// New messages added in future api updates should be added to the unused list above
Log.Trace("FxcmBrokerage.messageArrived(): Unknown message: {0}", message);
}
}
}
/// <summary>
/// TradingSessionStatus message handler
/// </summary>
private void OnTradingSessionStatus(TradingSessionStatus message)
{
if (message.getRequestID() == _currentRequest)
{
// load instrument list into a dictionary
var securities = message.getSecurities();
while (securities.hasMoreElements())
{
var security = (TradingSecurity)securities.nextElement();
_fxcmInstruments[security.getSymbol()] = security;
}
// get account base currency
AccountBaseCurrency = message.getParameter("BASE_CRNCY").getValue();
_mapRequestsToAutoResetEvents[_currentRequest].Set();
_mapRequestsToAutoResetEvents.Remove(_currentRequest);
}
}
/// <summary>
/// CollateralReport message handler
/// </summary>
private void OnCollateralReport(CollateralReport message)
{
// add the trading account to the account list
_accounts[message.getAccount()] = message;
if (message.getRequestID() == _currentRequest)
{
// set the state of the request to be completed only if this is the last collateral report requested
if (message.isLastRptRequested())
{
_mapRequestsToAutoResetEvents[_currentRequest].Set();
_mapRequestsToAutoResetEvents.Remove(_currentRequest);
}
}
}
/// <summary>
/// MarketDataSnapshot message handler
/// </summary>
private void OnMarketDataSnapshot(MarketDataSnapshot message)
{
var instrument = message.getInstrument();
var securityType = _symbolMapper.GetBrokerageSecurityType(instrument.getSymbol());
var symbol = _symbolMapper.GetLeanSymbol(instrument.getSymbol(), securityType, Market.FXCM);
var isHistoryResponse = _pendingHistoryRequests.Contains(message.getRequestID());
if (isHistoryResponse)
{
var time = FromJavaDate(message.getDate().toDate());
// history timestamps must be in exchange time zone
DateTimeZone exchangeTimeZone;
if (_symbolExchangeTimeZones.TryGetValue(symbol, out exchangeTimeZone))
{
time = time.ConvertFromUtc(exchangeTimeZone);
}
// append ticks/bars to history
if (message.getFXCMTimingInterval() == FXCMTimingIntervalFactory.TICK)
{
var bidPrice = Convert.ToDecimal(message.getBidClose());
var askPrice = Convert.ToDecimal(message.getAskClose());
var tick = new Tick(time, symbol, bidPrice, askPrice);
_lastHistoryChunk.Add(tick);
}
else
{
var bar = new QuoteBar(
time,
symbol,
new Bar(
Convert.ToDecimal(message.getBidOpen()),
Convert.ToDecimal(message.getBidHigh()),
Convert.ToDecimal(message.getBidLow()),
Convert.ToDecimal(message.getBidClose())
),
0,
new Bar(
Convert.ToDecimal(message.getAskOpen()),
Convert.ToDecimal(message.getAskHigh()),
Convert.ToDecimal(message.getAskLow()),
Convert.ToDecimal(message.getAskClose())
),
0);
_lastHistoryChunk.Add(bar);
}
}
else
{
// update the current prices for the instrument
_rates[instrument.getSymbol()] = message;
// if instrument is subscribed, add ticks to list
if (_subscriptionManager.IsSubscribed(symbol, TickType.Quote))
{
// For some unknown reason, messages returned by SubscriptionRequestTypeFactory.SUBSCRIBE
// have message.getDate() rounded to the second, so we use message.getMakingTime() instead
var time = FromJavaDate(new java.util.Date(message.getMakingTime()));
// live ticks timestamps must be in exchange time zone
DateTimeZone exchangeTimeZone;
if (_symbolExchangeTimeZones.TryGetValue(symbol, out exchangeTimeZone))
{
time = time.ConvertFromUtc(exchangeTimeZone);
}
var bidPrice = Convert.ToDecimal(message.getBidClose());
var askPrice = Convert.ToDecimal(message.getAskClose());
var tick = new Tick(time, symbol, bidPrice, askPrice);
_aggregator.Update(tick);
}
}
if (message.getRequestID() == _currentRequest)
{
if (message.getFXCMContinuousFlag() == IFixValueDefs.__Fields.FXCMCONTINUOUS_END)
{
_mapRequestsToAutoResetEvents[_currentRequest].Set();
_mapRequestsToAutoResetEvents.Remove(_currentRequest);
if (isHistoryResponse) _pendingHistoryRequests.Remove(_currentRequest);
}
}
}
/// <summary>
/// ExecutionReport message handler
/// </summary>
private void OnExecutionReport(ExecutionReport message)
{
var orderId = message.getOrderID();
var orderStatus = message.getFXCMOrdStatus();
if (orderId != "NONE" && message.getAccount() == _accountId)
{
if (_openOrders.ContainsKey(orderId) && OrderIsClosed(orderStatus.getCode()))
{
_openOrders.Remove(orderId);
}
else
{
_openOrders[orderId] = message;
}
Order order;
if (_mapFxcmOrderIdsToOrders.TryGetValue(orderId, out order))
{
// existing order
if (!OrderIsBeingProcessed(orderStatus.getCode()))
{
var status = ConvertOrderStatus(orderStatus);
int id;
// if we get a Submitted status and we had placed an order update, this new event is flagged as an update
var isUpdate = status == OrderStatus.Submitted && _orderUpdates.TryRemove(order.Id, out id);
var security = _securityProvider.GetSecurity(order.Symbol);
order.PriceCurrency = security.SymbolProperties.QuoteCurrency;
var orderEvent = new OrderEvent(order,
DateTime.UtcNow,
OrderFee.Zero)
{
Status = isUpdate ? OrderStatus.UpdateSubmitted : status,
FillPrice = Convert.ToDecimal(message.getPrice()),
FillQuantity = Convert.ToInt32(message.getSide() == SideFactory.BUY ? message.getLastQty() : -message.getLastQty()),
};
// we're catching the first fill so we apply the fees only once
if ((int)message.getCumQty() == (int)message.getLastQty() && message.getLastQty() > 0)
{
orderEvent.OrderFee = security.FeeModel.GetOrderFee(
new OrderFeeParameters(security, order));
}
_orderEventQueue.Enqueue(orderEvent);
}
}
else if (_mapRequestsToOrders.TryGetValue(message.getRequestID(), out order))
{
_mapFxcmOrderIdsToOrders[orderId] = order;
order.BrokerId.Add(orderId);
order.PriceCurrency = _securityProvider.GetSecurity(order.Symbol).SymbolProperties.QuoteCurrency;
// new order
var orderEvent = new OrderEvent(order,
DateTime.UtcNow,
OrderFee.Zero)
{
Status = ConvertOrderStatus(orderStatus)
};
_orderEventQueue.Enqueue(orderEvent);
}
}
if (message.getRequestID() == _currentRequest)
{
if (message.isLastRptRequested())
{
if (orderId == "NONE" && orderStatus.getCode() == IFixValueDefs.__Fields.FXCMORDSTATUS_REJECTED)
{
if (message.getSide() != SideFactory.UNDISCLOSED)
{
var messageText = message.getFXCMErrorDetails().Replace("\n", "");
Log.Trace("FxcmBrokerage.OnExecutionReport(): " + messageText);
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "OrderSubmitReject", messageText));
}
_isOrderSubmitRejected = true;
}
AutoResetEvent autoResetEvent;
if (_mapRequestsToAutoResetEvents.TryGetValue(_currentRequest, out autoResetEvent))
{
autoResetEvent.Set();
_mapRequestsToAutoResetEvents.Remove(_currentRequest);
}
}
}
}
/// <summary>
/// RequestForPositionsAck message handler
/// </summary>
private void OnRequestForPositionsAck(RequestForPositionsAck message)
{
if (message.getRequestID() == _currentRequest)
{
if (message.getTotalNumPosReports() == 0)
{
_mapRequestsToAutoResetEvents[_currentRequest].Set();
_mapRequestsToAutoResetEvents.Remove(_currentRequest);
}
}
}
/// <summary>
/// PositionReport message handler
/// </summary>
private void OnPositionReport(PositionReport message)
{
if (message.getAccount() == _accountId)
{
var fxcmPositionId = message.getFXCMPosID();
if (_openPositions.ContainsKey(fxcmPositionId) && message is ClosedPositionReport)
{
_openPositions.Remove(fxcmPositionId);
}
else
{
_openPositions[fxcmPositionId] = message;
}
}
if (message.getRequestID() == _currentRequest)
{
AutoResetEvent autoResetEvent;
if (message.isLastRptRequested() && _mapRequestsToAutoResetEvents.TryGetValue(_currentRequest, out autoResetEvent))
{
autoResetEvent.Set();
_mapRequestsToAutoResetEvents.Remove(_currentRequest);
}
}
}
/// <summary>
/// OrderCancelReject message handler
/// </summary>
private void OnOrderCancelReject(OrderCancelReject message)
{
if (message.getRequestID() == _currentRequest)
{
var messageText = message.getFXCMErrorDetails().Replace("\n", "");
Log.Trace("FxcmBrokerage.OnOrderCancelReject(): " + messageText);
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "OrderUpdateOrCancelReject", messageText));
_isOrderUpdateOrCancelRejected = true;
_mapRequestsToAutoResetEvents[_currentRequest].Set();
_mapRequestsToAutoResetEvents.Remove(_currentRequest);
}
}
#endregion
#region IStatusMessageListener implementation
/// <summary>
/// Receives status messages from the FXCM API
/// </summary>
/// <param name="message">Status message received</param>
public void messageArrived(ISessionStatus message)
{
switch (message.getStatusCode())
{
case ISessionStatus.__Fields.STATUSCODE_READY:
lock (_lockerConnectionMonitor)
{
_lastReadyMessageTime = DateTime.UtcNow;
}
break;
}
}
#endregion
}
}

View File

@@ -1,257 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using com.fxcm.entity;
using com.fxcm.fix;
using com.fxcm.fix.posttrade;
using com.fxcm.fix.trade;
using com.sun.rowset;
using java.util;
using NodaTime;
using QuantConnect.Orders;
namespace QuantConnect.Brokerages.Fxcm
{
/// <summary>
/// FXCM brokerage - private helper functions
/// </summary>
public partial class FxcmBrokerage
{
/// <summary>
/// Converts an FXCM order to a QuantConnect order.
/// </summary>
/// <param name="fxcmOrder">The FXCM order</param>
private Order ConvertOrder(ExecutionReport fxcmOrder)
{
Order order;
if (fxcmOrder.getOrdType() == OrdTypeFactory.LIMIT)
{
order = new LimitOrder
{
LimitPrice = Convert.ToDecimal(fxcmOrder.getPrice())
};
}
else if (fxcmOrder.getOrdType() == OrdTypeFactory.MARKET)
{
order = new MarketOrder();
}
else if (fxcmOrder.getOrdType() == OrdTypeFactory.STOP)
{
order = new StopMarketOrder
{
StopPrice = Convert.ToDecimal(fxcmOrder.getPrice())
};
}
else
{
throw new NotSupportedException("FxcmBrokerage.ConvertOrder(): The FXCM order type " + fxcmOrder.getOrdType() + " is not supported.");
}
var securityType = _symbolMapper.GetBrokerageSecurityType(fxcmOrder.getInstrument().getSymbol());
order.Symbol = _symbolMapper.GetLeanSymbol(fxcmOrder.getInstrument().getSymbol(), securityType, Market.FXCM);
order.Quantity = Convert.ToInt32(fxcmOrder.getOrderQty() * (fxcmOrder.getSide() == SideFactory.BUY ? +1 : -1));
order.Status = ConvertOrderStatus(fxcmOrder.getFXCMOrdStatus());
order.BrokerId.Add(fxcmOrder.getOrderID());
order.Properties.TimeInForce = ConvertTimeInForce(fxcmOrder.getTimeInForce());
order.Time = FromJavaDate(fxcmOrder.getTransactTime().toDate());
return order;
}
/// <summary>
/// Converts an FXCM order time in force to QuantConnect order time in force
/// </summary>
private static TimeInForce ConvertTimeInForce(ITimeInForce timeInForce)
{
if (timeInForce == TimeInForceFactory.GOOD_TILL_CANCEL)
return TimeInForce.GoodTilCanceled;
if (timeInForce == TimeInForceFactory.DAY)
return TimeInForce.Day;
throw new ArgumentOutOfRangeException();
}
/// <summary>
/// Converts an FXCM position to a QuantConnect holding.
/// </summary>
/// <param name="fxcmPosition">The FXCM position</param>
private Holding ConvertHolding(PositionReport fxcmPosition)
{
var securityType = _symbolMapper.GetBrokerageSecurityType(fxcmPosition.getInstrument().getSymbol());
return new Holding
{
Symbol = _symbolMapper.GetLeanSymbol(fxcmPosition.getInstrument().getSymbol(), securityType, Market.FXCM),
Type = securityType,
AveragePrice = Convert.ToDecimal(fxcmPosition.getSettlPrice()),
CurrencySymbol = "$",
Quantity = Convert.ToDecimal(fxcmPosition.getPositionQty().getLongQty() > 0
? fxcmPosition.getPositionQty().getLongQty()
: -fxcmPosition.getPositionQty().getShortQty())
};
}
/// <summary>
/// Converts an FXCM OrderStatus to a QuantConnect <see cref="OrderStatus"/>
/// </summary>
/// <param name="status"></param>
/// <returns></returns>
private static OrderStatus ConvertOrderStatus(ICode status)
{
var result = OrderStatus.None;
switch (status.getCode())
{
case IFixValueDefs.__Fields.FXCMORDSTATUS_INPROCESS:
case IFixValueDefs.__Fields.FXCMORDSTATUS_WAITING:
case IFixValueDefs.__Fields.FXCMORDSTATUS_EXECUTING:
result = OrderStatus.Submitted;
break;
case IFixValueDefs.__Fields.FXCMORDSTATUS_EXECUTED:
result = OrderStatus.Filled;
break;
case IFixValueDefs.__Fields.FXCMORDSTATUS_CANCELLED:
case IFixValueDefs.__Fields.FXCMORDSTATUS_EXPIRED:
result = OrderStatus.Canceled;
break;
case IFixValueDefs.__Fields.FXCMORDSTATUS_REJECTED:
result = OrderStatus.Invalid;
break;
}
return result;
}
/// <summary>
/// Returns true if the specified order is considered open, otherwise false
/// </summary>
private static bool OrderIsOpen(string orderStatus)
{
return orderStatus == IFixValueDefs.__Fields.FXCMORDSTATUS_WAITING;
}
/// <summary>
/// Returns true if the specified order is considered close, otherwise false
/// </summary>
protected static bool OrderIsClosed(string orderStatus)
{
return orderStatus == IFixValueDefs.__Fields.FXCMORDSTATUS_EXECUTED
|| orderStatus == IFixValueDefs.__Fields.FXCMORDSTATUS_CANCELLED
|| orderStatus == IFixValueDefs.__Fields.FXCMORDSTATUS_EXPIRED
|| orderStatus == IFixValueDefs.__Fields.FXCMORDSTATUS_REJECTED;
}
/// <summary>
/// Returns true if the specified order is being processed, otherwise false
/// </summary>
private static bool OrderIsBeingProcessed(string orderStatus)
{
return !OrderIsOpen(orderStatus) && !OrderIsClosed(orderStatus);
}
/// <summary>
/// Converts a Java Date value to a UTC DateTime value
/// </summary>
/// <param name="javaDate">The Java date</param>
/// <returns></returns>
private static DateTime FromJavaDate(Date javaDate)
{
// Convert javaDate to UTC Instant (Epoch)
var instant = Instant.FromUnixTimeMilliseconds(javaDate.getTime());
// Convert to .Net UTC DateTime
return instant.ToDateTimeUtc();
}
/// <summary>
/// Converts a LEAN Resolution to an IFXCMTimingInterval
/// </summary>
/// <param name="resolution">The resolution to convert</param>
/// <returns></returns>
public static IFXCMTimingInterval ToFxcmInterval(Resolution resolution)
{
IFXCMTimingInterval interval = null;
switch (resolution)
{
case Resolution.Tick:
interval = FXCMTimingIntervalFactory.TICK;
break;
case Resolution.Second:
interval = FXCMTimingIntervalFactory.SEC10;
break;
case Resolution.Minute:
interval = FXCMTimingIntervalFactory.MIN1;
break;
case Resolution.Hour:
interval = FXCMTimingIntervalFactory.HOUR1;
break;
case Resolution.Daily:
interval = FXCMTimingIntervalFactory.DAY1;
break;
}
return interval;
}
/// <summary>
/// Converts a Java Date value to a UTC DateTime value
/// </summary>
/// <param name="utcDateTime">The UTC DateTime value</param>
/// <returns>A UTC Java Date value</returns>
public static Date ToJavaDateUtc(DateTime utcDateTime)
{
var cal = Calendar.getInstance();
cal.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));
cal.set(Calendar.YEAR, utcDateTime.Year);
cal.set(Calendar.MONTH, utcDateTime.Month - 1);
cal.set(Calendar.DAY_OF_MONTH, utcDateTime.Day);
cal.set(Calendar.HOUR_OF_DAY, utcDateTime.Hour);
cal.set(Calendar.MINUTE, utcDateTime.Minute);
cal.set(Calendar.SECOND, utcDateTime.Second);
cal.set(Calendar.MILLISECOND, utcDateTime.Millisecond);
return cal.getTime();
}
//
// So it turns out that in order to properly load the QuantConnect.Brokerages
// dll we need the IKVM.OpenJdk.Jdbc referenced in other projects that use
// this. By placing a hard reference to an IKVM.OpenJdk.Jdbc type, the compiler
// will properly copy the required dlls into other project bin directories.
// Without this, consuming projects would need to hard refernce the IKVM dlls,
// which is less than perfect. This seems to be the better of two evils
//
private static void ManageIKVMDependency()
{
var rowset = new CachedRowSetImpl();
}
}
}

View File

@@ -1,781 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using com.fxcm.external.api.transport;
using com.fxcm.external.api.transport.listeners;
using com.fxcm.external.api.util;
using com.fxcm.fix;
using com.fxcm.fix.pretrade;
using com.fxcm.fix.trade;
using com.fxcm.messaging.util;
using NodaTime;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Logging;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace QuantConnect.Brokerages.Fxcm
{
/// <summary>
/// FXCM brokerage - implementation of IBrokerage interface
/// </summary>
[BrokerageFactory(typeof(FxcmBrokerageFactory))]
public partial class FxcmBrokerage : Brokerage, IDataQueueHandler, IGenericMessageListener, IStatusMessageListener
{
private readonly IOrderProvider _orderProvider;
private readonly ISecurityProvider _securityProvider;
private readonly IDataAggregator _aggregator;
private readonly string _server;
private readonly string _terminal;
private readonly string _userName;
private readonly string _password;
private readonly string _accountId;
private Thread _orderEventThread;
private Thread _connectionMonitorThread;
private readonly object _lockerConnectionMonitor = new object();
private DateTime _lastReadyMessageTime;
private volatile bool _connectionLost;
// tracks requested order updates, so we can flag Submitted order events as updates
private readonly ConcurrentDictionary<int, int> _orderUpdates = new ConcurrentDictionary<int, int>();
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private readonly ConcurrentQueue<OrderEvent> _orderEventQueue = new ConcurrentQueue<OrderEvent>();
private readonly FxcmSymbolMapper _symbolMapper = new FxcmSymbolMapper();
private readonly EventBasedDataQueueHandlerSubscriptionManager _subscriptionManager;
private readonly IList<BaseData> _lastHistoryChunk = new List<BaseData>();
private readonly Dictionary<Symbol, DateTimeZone> _symbolExchangeTimeZones = new Dictionary<Symbol, DateTimeZone>();
/// <summary>
/// Gets/sets a timeout for history requests (in milliseconds)
/// </summary>
public int HistoryResponseTimeout { get; set; }
/// <summary>
/// Gets/sets the maximum number of retries for a history request
/// </summary>
public int MaximumHistoryRetryAttempts { get; set; }
/// <summary>
/// Gets/sets a value to enable only history requests to this brokerage
/// Set to true in parallel downloaders to avoid loading accounts, orders, positions etc. at connect time
/// </summary>
public bool EnableOnlyHistoryRequests { get; set; }
/// <summary>
/// Static constructor for the <see cref="FxcmBrokerage"/> class
/// </summary>
static FxcmBrokerage()
{
// FXCM requires TLS 1.2 since 6/16/2019
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
}
/// <summary>
/// Creates a new instance of the <see cref="FxcmBrokerage"/> class
/// </summary>
/// <param name="orderProvider">The order provider</param>
/// <param name="securityProvider">The holdings provider</param>
/// <param name="aggregator">Consolidate ticks</param>
/// <param name="server">The url of the server</param>
/// <param name="terminal">The terminal name</param>
/// <param name="userName">The user name (login id)</param>
/// <param name="password">The user password</param>
/// <param name="accountId">The account id</param>
public FxcmBrokerage(IOrderProvider orderProvider, ISecurityProvider securityProvider, IDataAggregator aggregator, string server, string terminal, string userName, string password, string accountId)
: base("FXCM Brokerage")
{
_orderProvider = orderProvider;
_securityProvider = securityProvider;
_aggregator = aggregator;
_server = server;
_terminal = terminal;
_userName = userName;
_password = password;
_accountId = accountId;
_subscriptionManager = new EventBasedDataQueueHandlerSubscriptionManager();
_subscriptionManager.SubscribeImpl += (s, t) => Subscribe(s);
_subscriptionManager.UnsubscribeImpl += (s, t) => Unsubscribe(s);
HistoryResponseTimeout = 5000;
MaximumHistoryRetryAttempts = 1;
}
#region IBrokerage implementation
/// <summary>
/// Returns true if we're currently connected to the broker
/// </summary>
public override bool IsConnected
{
get
{
return _gateway != null && _gateway.isConnected() && !_connectionLost;
}
}
/// <summary>
/// Connects the client to the broker's remote servers
/// </summary>
public override void Connect()
{
if (IsConnected) return;
Log.Trace("FxcmBrokerage.Connect()");
_cancellationTokenSource = new CancellationTokenSource();
// create new thread to fire order events in queue
if (!EnableOnlyHistoryRequests)
{
_orderEventThread = new Thread(() =>
{
while (!_cancellationTokenSource.IsCancellationRequested)
{
try
{
OrderEvent orderEvent;
if (!_orderEventQueue.TryDequeue(out orderEvent))
{
Thread.Sleep(1);
continue;
}
OnOrderEvent(orderEvent);
}
catch (Exception exception)
{
Log.Error(exception);
}
}
}) { IsBackground = true };
_orderEventThread.Start();
while (!_orderEventThread.IsAlive)
{
Thread.Sleep(1);
}
}
// create the gateway
_gateway = GatewayFactory.createGateway();
// register the message listeners with the gateway
_gateway.registerGenericMessageListener(this);
_gateway.registerStatusMessageListener(this);
// create local login properties
var loginProperties = new FXCMLoginProperties(_userName, _password, _terminal, _server);
loginProperties.addProperty(IConnectionManager.APP_INFO, "QuantConnect");
// log in
try
{
_gateway.login(loginProperties);
}
catch (Exception err)
{
var message =
err.Message.Contains("ORA-20101") ? "Incorrect login credentials" :
err.Message.Contains("ORA-20003") ? "Contact api@fxcm.com to enable API access, below is a template email. " + Environment.NewLine +
"Email: api@fxcm.com " + Environment.NewLine +
"Template: " + Environment.NewLine +
"Hello FXCM staff, " + Environment.NewLine +
"Please enable Java API for all accounts which are associated with this email address. " + Environment.NewLine +
"Also, please respond to this email address once Java API has been enabled, letting me know that the change was done successfully." :
err.Message;
_cancellationTokenSource.Cancel();
throw new BrokerageException(message, err.InnerException);
}
// create new thread to manage disconnections and reconnections
if (!EnableOnlyHistoryRequests)
{
_connectionMonitorThread = new Thread(() =>
{
_lastReadyMessageTime = DateTime.UtcNow;
try
{
while (!_cancellationTokenSource.IsCancellationRequested)
{
TimeSpan elapsed;
lock (_lockerConnectionMonitor)
{
elapsed = DateTime.UtcNow - _lastReadyMessageTime;
}
if (!_connectionLost && elapsed > TimeSpan.FromSeconds(10))
{
_connectionLost = true;
OnMessage(BrokerageMessageEvent.Disconnected("Connection with FXCM server lost. " +
"This could be because of internet connectivity issues. "));
}
else if (_connectionLost && IsWithinTradingHours())
{
Log.Trace("FxcmBrokerage.ConnectionMonitorThread(): Attempting reconnection...");
try
{
// log out
try
{
_gateway.logout();
}
catch (Exception)
{
// ignored
}
// remove the message listeners
_gateway.removeGenericMessageListener(this);
_gateway.removeStatusMessageListener(this);
// register the message listeners with the gateway
_gateway.registerGenericMessageListener(this);
_gateway.registerStatusMessageListener(this);
// log in
_gateway.login(loginProperties);
// load instruments, accounts, orders, positions
LoadInstruments();
if (!EnableOnlyHistoryRequests)
{
LoadAccounts();
LoadOpenOrders();
LoadOpenPositions();
}
_connectionLost = false;
OnMessage(BrokerageMessageEvent.Reconnected("Connection with FXCM server restored."));
}
catch (Exception exception)
{
Log.Trace("FxcmBrokerage.ConnectionMonitorThread(): reconnect failed.");
Log.Error(exception);
}
}
Thread.Sleep(5000);
}
}
catch (Exception exception)
{
Log.Error(exception);
}
}) { IsBackground = true };
_connectionMonitorThread.Start();
while (!_connectionMonitorThread.IsAlive)
{
Thread.Sleep(1);
}
}
// load instruments, accounts, orders, positions
LoadInstruments();
if (!EnableOnlyHistoryRequests)
{
LoadAccounts();
LoadOpenOrders();
LoadOpenPositions();
}
}
/// <summary>
/// Returns true if we are within FXCM trading hours
/// </summary>
/// <returns></returns>
private static bool IsWithinTradingHours()
{
var time = DateTime.UtcNow.ConvertFromUtc(TimeZones.EasternStandard);
// FXCM Trading Hours: http://help.fxcm.com/us/Trading-Basics/New-to-Forex/38757093/What-are-the-Trading-Hours.htm
return !(time.DayOfWeek == DayOfWeek.Friday && time.TimeOfDay > new TimeSpan(16, 55, 0) ||
time.DayOfWeek == DayOfWeek.Saturday ||
time.DayOfWeek == DayOfWeek.Sunday && time.TimeOfDay < new TimeSpan(17, 0, 0) ||
time.Month == 12 && time.Day == 25 ||
time.Month == 1 && time.Day == 1);
}
/// <summary>
/// Disconnects the client from the broker's remote servers
/// </summary>
public override void Disconnect()
{
Log.Trace("FxcmBrokerage.Disconnect()");
if (_gateway != null)
{
// log out
try
{
if (_gateway.isConnected())
_gateway.logout();
}
catch (Exception)
{
// ignored
}
// remove the message listeners
_gateway.removeGenericMessageListener(this);
_gateway.removeStatusMessageListener(this);
}
// request and wait for thread to stop
if (_cancellationTokenSource != null) _cancellationTokenSource.Cancel();
if (!EnableOnlyHistoryRequests)
{
if (_orderEventThread != null) _orderEventThread.Join();
if (_connectionMonitorThread != null) _connectionMonitorThread.Join();
}
}
/// <summary>
/// Gets all open orders on the account.
/// NOTE: The order objects returned do not have QC order IDs.
/// </summary>
/// <returns>The open orders returned from FXCM</returns>
public override List<Order> GetOpenOrders()
{
Log.Trace($"FxcmBrokerage.GetOpenOrders(): Located {_openOrders.Count.ToStringInvariant()} orders");
var orders = _openOrders.Values.ToList()
.Where(x => OrderIsOpen(x.getFXCMOrdStatus().getCode()))
.Select(ConvertOrder)
.ToList();
return orders;
}
/// <summary>
/// Gets all holdings for the account
/// </summary>
/// <returns>The current holdings from the account</returns>
public override List<Holding> GetAccountHoldings()
{
Log.Trace("FxcmBrokerage.GetAccountHoldings()");
// FXCM maintains multiple positions per symbol, so we aggregate them by symbol.
// The average price for the aggregated position is the quantity weighted average price.
var holdings = _openPositions.Values
.Select(ConvertHolding)
.Where(x => x.Quantity != 0)
.GroupBy(x => x.Symbol)
.Select(group => new Holding
{
Symbol = group.Key,
Type = group.First().Type,
AveragePrice = group.Sum(x => x.AveragePrice * x.Quantity) / group.Sum(x => x.Quantity),
CurrencySymbol = group.First().CurrencySymbol,
Quantity = group.Sum(x => x.Quantity)
})
.ToList();
// Set MarketPrice in each Holding
var fxcmSymbols = holdings
.Select(x => _symbolMapper.GetBrokerageSymbol(x.Symbol))
.ToList();
if (fxcmSymbols.Count > 0)
{
var quotes = GetQuotes(fxcmSymbols).ToDictionary(x => x.getInstrument().getSymbol());
foreach (var holding in holdings)
{
MarketDataSnapshot quote;
if (quotes.TryGetValue(_symbolMapper.GetBrokerageSymbol(holding.Symbol), out quote))
{
holding.MarketPrice = Convert.ToDecimal((quote.getBidClose() + quote.getAskClose()) / 2);
}
}
}
return holdings;
}
/// <summary>
/// Gets the current cash balance for each currency held in the brokerage account
/// </summary>
/// <returns>The current cash balance for each currency available for trading</returns>
public override List<CashAmount> GetCashBalance()
{
Log.Trace("FxcmBrokerage.GetCashBalance()");
var cashBook = new List<CashAmount>();
//Adds the account currency to the cashbook.
cashBook.Add(new CashAmount(Convert.ToDecimal(_accounts[_accountId].getCashOutstanding()),
AccountBaseCurrency));
// include cash balances from currency swaps for open Forex positions
foreach (var trade in _openPositions.Values)
{
var brokerageSymbol = trade.getInstrument().getSymbol();
var ticker = FxcmSymbolMapper.ConvertFxcmSymbolToLeanSymbol(brokerageSymbol);
var securityType = _symbolMapper.GetBrokerageSecurityType(brokerageSymbol);
if (securityType == SecurityType.Forex)
{
//settlement price for the trade
var settlementPrice = Convert.ToDecimal(trade.getSettlPrice());
//direction of trade
var direction = trade.getPositionQty().getLongQty() > 0 ? 1 : -1;
//quantity of the asset
var quantity = Convert.ToDecimal(trade.getPositionQty().getQty());
//quantity of base currency
var baseQuantity = direction * quantity;
//quantity of quote currency
var quoteQuantity = -direction * quantity * settlementPrice;
//base currency
var baseCurrency = trade.getCurrency();
//quote currency
var quoteCurrency = ticker.Substring(ticker.Length - 3);
var baseCurrencyAmount = cashBook.FirstOrDefault(x => x.Currency == baseCurrency);
//update the value of the base currency
if (baseCurrencyAmount != default(CashAmount))
{
cashBook.Remove(baseCurrencyAmount);
cashBook.Add(new CashAmount(baseQuantity + baseCurrencyAmount.Amount, baseCurrency));
}
else
{
//add the base currency if not present
cashBook.Add(new CashAmount(baseQuantity, baseCurrency));
}
var quoteCurrencyAmount = cashBook.Find(x => x.Currency == quoteCurrency);
//update the value of the quote currency
if (quoteCurrencyAmount != default(CashAmount))
{
cashBook.Remove(quoteCurrencyAmount);
cashBook.Add(new CashAmount(quoteQuantity + quoteCurrencyAmount.Amount, quoteCurrency));
}
else
{
//add the quote currency if not present
cashBook.Add(new CashAmount(quoteQuantity, quoteCurrency));
}
}
}
return cashBook;
}
/// <summary>
/// Places a new order and assigns a new broker ID to the order
/// </summary>
/// <param name="order">The order to be placed</param>
/// <returns>True if the request for a new order has been placed, false otherwise</returns>
public override bool PlaceOrder(Order order)
{
Log.Trace("FxcmBrokerage.PlaceOrder(): {0}", order);
if (!IsConnected)
throw new InvalidOperationException("FxcmBrokerage.PlaceOrder(): Unable to place order while not connected.");
if (order.Direction != OrderDirection.Buy && order.Direction != OrderDirection.Sell)
throw new ArgumentException("FxcmBrokerage.PlaceOrder(): Invalid Order Direction");
var fxcmSymbol = _symbolMapper.GetBrokerageSymbol(order.Symbol);
var orderSide = order.Direction == OrderDirection.Buy ? SideFactory.BUY : SideFactory.SELL;
var quantity = (double)order.AbsoluteQuantity;
OrderSingle orderRequest;
switch (order.Type)
{
case OrderType.Market:
orderRequest = MessageGenerator.generateMarketOrder(_accountId, quantity, orderSide, fxcmSymbol, "");
break;
case OrderType.Limit:
var limitPrice = (double)((LimitOrder)order).LimitPrice;
orderRequest = MessageGenerator.generateOpenOrder(limitPrice, _accountId, quantity, orderSide, fxcmSymbol, "");
orderRequest.setOrdType(OrdTypeFactory.LIMIT);
orderRequest.setTimeInForce(TimeInForceFactory.GOOD_TILL_CANCEL);
break;
case OrderType.StopMarket:
var stopPrice = (double)((StopMarketOrder)order).StopPrice;
orderRequest = MessageGenerator.generateOpenOrder(stopPrice, _accountId, quantity, orderSide, fxcmSymbol, "");
orderRequest.setOrdType(OrdTypeFactory.STOP);
orderRequest.setTimeInForce(TimeInForceFactory.GOOD_TILL_CANCEL);
break;
default:
throw new NotSupportedException("FxcmBrokerage.PlaceOrder(): Order type " + order.Type + " is not supported.");
}
_isOrderSubmitRejected = false;
AutoResetEvent autoResetEvent;
lock (_locker)
{
_currentRequest = _gateway.sendMessage(orderRequest);
_mapRequestsToOrders[_currentRequest] = order;
autoResetEvent = new AutoResetEvent(false);
_mapRequestsToAutoResetEvents[_currentRequest] = autoResetEvent;
}
if (!autoResetEvent.WaitOne(ResponseTimeout))
throw new TimeoutException("FxcmBrokerage.PlaceOrder(): Operation took longer than " +
$"{((decimal) ResponseTimeout / 1000).ToStringInvariant()} seconds."
);
return !_isOrderSubmitRejected;
}
/// <summary>
/// Updates the order with the same id
/// </summary>
/// <param name="order">The new order information</param>
/// <returns>True if the request was made for the order to be updated, false otherwise</returns>
public override bool UpdateOrder(Order order)
{
Log.Trace("FxcmBrokerage.UpdateOrder(): {0}", order);
if (!IsConnected)
throw new InvalidOperationException("FxcmBrokerage.UpdateOrder(): Unable to update order while not connected.");
if (!order.BrokerId.Any())
{
// we need the brokerage order id in order to perform an update
Log.Trace("FxcmBrokerage.UpdateOrder(): Unable to update order without BrokerId.");
return false;
}
var fxcmOrderId = order.BrokerId[0].ToStringInvariant();
ExecutionReport fxcmOrder;
if (!_openOrders.TryGetValue(fxcmOrderId, out fxcmOrder))
throw new ArgumentException("FxcmBrokerage.UpdateOrder(): FXCM order id not found: " + fxcmOrderId);
double price;
switch (order.Type)
{
case OrderType.Limit:
price = (double)((LimitOrder)order).LimitPrice;
break;
case OrderType.StopMarket:
price = (double)((StopMarketOrder)order).StopPrice;
break;
default:
throw new NotSupportedException("FxcmBrokerage.UpdateOrder(): Invalid order type.");
}
_isOrderUpdateOrCancelRejected = false;
var orderReplaceRequest = MessageGenerator.generateOrderReplaceRequest("", fxcmOrder.getOrderID(), fxcmOrder.getSide(), fxcmOrder.getOrdType(), price, fxcmOrder.getAccount());
orderReplaceRequest.setInstrument(fxcmOrder.getInstrument());
orderReplaceRequest.setOrderQty((double)order.AbsoluteQuantity);
AutoResetEvent autoResetEvent;
lock (_locker)
{
_orderUpdates[order.Id] = order.Id;
_currentRequest = _gateway.sendMessage(orderReplaceRequest);
autoResetEvent = new AutoResetEvent(false);
_mapRequestsToAutoResetEvents[_currentRequest] = autoResetEvent;
}
if (!autoResetEvent.WaitOne(ResponseTimeout))
throw new TimeoutException("FxcmBrokerage.UpdateOrder(): Operation took longer than " +
$"{((decimal) ResponseTimeout / 1000).ToStringInvariant()} seconds."
);
return !_isOrderUpdateOrCancelRejected;
}
/// <summary>
/// Cancels the order with the specified ID
/// </summary>
/// <param name="order">The order to cancel</param>
/// <returns>True if the request was made for the order to be canceled, false otherwise</returns>
public override bool CancelOrder(Order order)
{
Log.Trace("FxcmBrokerage.CancelOrder(): {0}", order);
if (!IsConnected)
throw new InvalidOperationException("FxcmBrokerage.UpdateOrder(): Unable to cancel order while not connected.");
if (!order.BrokerId.Any())
{
// we need the brokerage order id in order to perform a cancellation
Log.Trace("FxcmBrokerage.CancelOrder(): Unable to cancel order without BrokerId.");
return false;
}
var fxcmOrderId = order.BrokerId[0].ToStringInvariant();
ExecutionReport fxcmOrder;
if (!_openOrders.TryGetValue(fxcmOrderId, out fxcmOrder))
throw new ArgumentException("FxcmBrokerage.CancelOrder(): FXCM order id not found: " + fxcmOrderId);
_isOrderUpdateOrCancelRejected = false;
var orderCancelRequest = MessageGenerator.generateOrderCancelRequest("", fxcmOrder.getOrderID(), fxcmOrder.getSide(), fxcmOrder.getAccount());
AutoResetEvent autoResetEvent;
lock (_locker)
{
_currentRequest = _gateway.sendMessage(orderCancelRequest);
autoResetEvent = new AutoResetEvent(false);
_mapRequestsToAutoResetEvents[_currentRequest] = autoResetEvent;
}
if (!autoResetEvent.WaitOne(ResponseTimeout))
throw new TimeoutException("FxcmBrokerage.CancelOrder(): Operation took longer than " +
$"{((decimal) ResponseTimeout / 1000).ToStringInvariant()} seconds."
);
return !_isOrderUpdateOrCancelRejected;
}
/// <summary>
/// Gets the history for the requested security
/// </summary>
/// <param name="request">The historical data request</param>
/// <returns>An enumerable of bars covering the span specified in the request</returns>
public override IEnumerable<BaseData> GetHistory(HistoryRequest request)
{
if (!_symbolMapper.IsKnownLeanSymbol(request.Symbol))
{
Log.Trace("FxcmBrokerage.GetHistory(): Invalid symbol: {0}, no history returned", request.Symbol.Value);
yield break;
}
// cache exchange time zone for symbol
DateTimeZone exchangeTimeZone;
if (!_symbolExchangeTimeZones.TryGetValue(request.Symbol, out exchangeTimeZone))
{
exchangeTimeZone = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.FXCM, request.Symbol, request.Symbol.SecurityType).TimeZone;
_symbolExchangeTimeZones.Add(request.Symbol, exchangeTimeZone);
}
var interval = ToFxcmInterval(request.Resolution);
// download data
var history = new List<BaseData>();
var lastEndTime = DateTime.MinValue;
var end = request.EndTimeUtc;
var attempt = 1;
while (end > request.StartTimeUtc)
{
Log.Debug($"FxcmBrokerage.GetHistory(): Requesting {end.ToIso8601Invariant()} to {request.StartTimeUtc.ToIso8601Invariant()}");
_lastHistoryChunk.Clear();
var mdr = new MarketDataRequest();
mdr.setSubscriptionRequestType(SubscriptionRequestTypeFactory.SNAPSHOT);
mdr.setResponseFormat(IFixMsgTypeDefs.__Fields.MSGTYPE_FXCMRESPONSE);
mdr.setFXCMTimingInterval(interval);
mdr.setMDEntryTypeSet(MarketDataRequest.MDENTRYTYPESET_ALL);
mdr.setFXCMStartDate(new UTCDate(ToJavaDateUtc(request.StartTimeUtc)));
mdr.setFXCMStartTime(new UTCTimeOnly(ToJavaDateUtc(request.StartTimeUtc)));
mdr.setFXCMEndDate(new UTCDate(ToJavaDateUtc(end)));
mdr.setFXCMEndTime(new UTCTimeOnly(ToJavaDateUtc(end)));
mdr.addRelatedSymbol(_fxcmInstruments[_symbolMapper.GetBrokerageSymbol(request.Symbol)]);
AutoResetEvent autoResetEvent;
lock (_locker)
{
_currentRequest = _gateway.sendMessage(mdr);
autoResetEvent = new AutoResetEvent(false);
_mapRequestsToAutoResetEvents[_currentRequest] = autoResetEvent;
_pendingHistoryRequests.Add(_currentRequest);
}
if (!autoResetEvent.WaitOne(HistoryResponseTimeout))
{
// No response can mean genuine timeout or the history data has ended.
// 90% of the time no response because no data; widen the search net to 5m if we don't get a response:
if (request.StartTimeUtc.AddSeconds(300) >= end)
{
break;
}
// 5% of the time its because the data ends at a specific, repeatible time not close to our desired endtime:
if (end == lastEndTime)
{
Log.Trace("FxcmBrokerage.GetHistory(): Request for {0} ended at {1:O}", request.Symbol.Value, end);
break;
}
// 5% of the time its because of an internet / time of day / api settings / timeout: throw if this is the *second* attempt.
if (EnableOnlyHistoryRequests && lastEndTime != DateTime.MinValue)
{
throw new TimeoutException("FxcmBrokerage.GetHistory(): History operation ending in {end:O} took longer than " +
$"{((decimal) HistoryResponseTimeout / 1000).ToStringInvariant()} seconds. This may be because there is no data, retrying..."
);
}
// Assuming Timeout: If we've already retried quite a few times, lets bail.
if (++attempt > MaximumHistoryRetryAttempts)
{
Log.Trace("FxcmBrokerage.GetHistory(): Maximum attempts reached for: " + request.Symbol.Value);
break;
}
// Assuming Timeout: Save end time and if have the same endtime next time, break since its likely there's no data after that time.
lastEndTime = end;
Log.Trace($"FxcmBrokerage.GetHistory(): Attempt {attempt.ToStringInvariant()} for: " +
$"{request.Symbol.Value} ended at {lastEndTime.ToIso8601Invariant()}"
);
continue;
}
// Add data
lock (_locker)
{
history.InsertRange(0, _lastHistoryChunk);
}
var firstDateUtc = _lastHistoryChunk[0].Time.ConvertToUtc(exchangeTimeZone);
if (end != firstDateUtc)
{
// new end date = first datapoint date.
end = request.Resolution == Resolution.Tick ? firstDateUtc.AddMilliseconds(-1) : firstDateUtc.AddSeconds(-1);
if (request.StartTimeUtc.AddSeconds(10) >= end)
break;
}
else
{
break;
}
}
foreach (var data in history)
{
yield return data;
}
}
#endregion
}
}

View File

@@ -1,125 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using QuantConnect.Configuration;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Packets;
using QuantConnect.Securities;
using QuantConnect.Util;
namespace QuantConnect.Brokerages.Fxcm
{
/// <summary>
/// Provides an implementation of <see cref="IBrokerageFactory"/> that produces a <see cref="FxcmBrokerage"/>
/// </summary>
public class FxcmBrokerageFactory : BrokerageFactory
{
private const string DefaultServer = "http://www.fxcorporate.com/Hosts.jsp";
private const string DefaultTerminal = "Demo";
/// <summary>
/// Initializes a new instance of the <see cref="FxcmBrokerageFactory"/> class
/// </summary>
public FxcmBrokerageFactory()
: base(typeof(FxcmBrokerage))
{
}
/// <summary>
/// Gets the brokerage data required to run the brokerage from configuration/disk
/// </summary>
/// <remarks>
/// The implementation of this property will create the brokerage data dictionary required for
/// running live jobs. See <see cref="IJobQueueHandler.NextJob"/>
/// </remarks>
public override Dictionary<string, string> BrokerageData
{
get
{
return new Dictionary<string, string>
{
{ "fxcm-server", Config.Get("fxcm-server", DefaultServer) },
{ "fxcm-terminal", Config.Get("fxcm-terminal", DefaultTerminal) },
{ "fxcm-user-name", Config.Get("fxcm-user-name") },
{ "fxcm-password", Config.Get("fxcm-password") },
{ "fxcm-account-id", Config.Get("fxcm-account-id") }
};
}
}
/// <summary>
/// Gets a new instance of the <see cref="FxcmBrokerageModel"/>
/// </summary>
/// <param name="orderProvider">The order provider</param>
public override IBrokerageModel GetBrokerageModel(IOrderProvider orderProvider) => new FxcmBrokerageModel();
/// <summary>
/// Creates a new <see cref="IBrokerage"/> instance
/// </summary>
/// <param name="job">The job packet to create the brokerage for</param>
/// <param name="algorithm">The algorithm instance</param>
/// <returns>A new brokerage instance</returns>
public override IBrokerage CreateBrokerage(LiveNodePacket job, IAlgorithm algorithm)
{
var errors = new List<string>();
// read values from the brokerage data
var server = Read<string>(job.BrokerageData, "fxcm-server", errors);
var terminal = Read<string>(job.BrokerageData, "fxcm-terminal", errors);
var userName = Read<string>(job.BrokerageData, "fxcm-user-name", errors);
var password = Read<string>(job.BrokerageData, "fxcm-password", errors);
var accountId = Read<string>(job.BrokerageData, "fxcm-account-id", errors);
if (errors.Count != 0)
{
// if we had errors then we can't create the instance
throw new Exception(string.Join(Environment.NewLine, errors));
}
var brokerage = new FxcmBrokerage(
algorithm.Transactions,
algorithm.Portfolio,
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager")),
server,
terminal,
userName,
password,
accountId);
Composer.Instance.AddPart<IDataQueueHandler>(brokerage);
return brokerage;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <filterpriority>2</filterpriority>
public override void Dispose()
{
}
/// <summary>
/// Gets a brokerage message handler
/// </summary>
public override IBrokerageMessageHandler CreateBrokerageMessageHandler(IAlgorithm algorithm, AlgorithmNodePacket job, IApi api)
{
//We have chosen a timespan of negative 30 beacause FXCM market hours don't always open on time.
return new DefaultBrokerageMessageHandler(algorithm, job, api, openThreshold: TimeSpan.FromMinutes(-30));
}
}
}

View File

@@ -1,267 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
namespace QuantConnect.Brokerages.Fxcm
{
/// <summary>
/// Provides the mapping between Lean symbols and FXCM symbols.
/// </summary>
public class FxcmSymbolMapper : ISymbolMapper
{
/// <summary>
/// List of all known symbols for FXCM
/// </summary>
public static List<Symbol> KnownSymbols
{
get
{
var symbols = new List<Symbol>();
var mapper = new FxcmSymbolMapper();
foreach (var tp in FxcmSymbolMappings)
{
symbols.Add(mapper.GetLeanSymbol(tp.Item1, mapper.GetBrokerageSecurityType(tp.Item1), Market.FXCM));
}
return symbols;
}
}
/// <summary>
/// Helper class to allow collection initializer on a List of tuples
/// </summary>
private class TupleList<T1, T2> : List<Tuple<T1, T2>>
{
public void Add(T1 item1, T2 item2)
{
Add(new Tuple<T1, T2>(item1, item2));
}
}
/// <summary>
/// The list of mappings from FXCM symbols to Lean symbols.
/// </summary>
/// <remarks>T1 is FXCM symbol, T2 is Lean symbol</remarks>
private static readonly TupleList<string, string> FxcmSymbolMappings = new TupleList<string, string>
{
{ "AUD/CAD", "AUDCAD" },
{ "AUD/CHF", "AUDCHF" },
{ "AUD/JPY", "AUDJPY" },
{ "AUD/NZD", "AUDNZD" },
{ "AUD/USD", "AUDUSD" },
{ "AUS200", "AU200AUD" },
{ "Bund", "DE10YBEUR" },
{ "CAD/CHF", "CADCHF" },
{ "CAD/JPY", "CADJPY" },
{ "CHF/JPY", "CHFJPY" },
{ "Copper", "XCUUSD" },
{ "ESP35", "ES35EUR" },
{ "EUR/AUD", "EURAUD" },
{ "EUR/CAD", "EURCAD" },
{ "EUR/CHF", "EURCHF" },
{ "EUR/GBP", "EURGBP" },
{ "EUR/JPY", "EURJPY" },
{ "EUR/NOK", "EURNOK" },
{ "EUR/NZD", "EURNZD" },
{ "EUR/SEK", "EURSEK" },
{ "EUR/TRY", "EURTRY" },
{ "EUR/USD", "EURUSD" },
{ "EUSTX50", "EU50EUR" },
{ "FRA40", "FR40EUR" },
{ "GBP/AUD", "GBPAUD" },
{ "GBP/CAD", "GBPCAD" },
{ "GBP/CHF", "GBPCHF" },
{ "GBP/JPY", "GBPJPY" },
{ "GBP/NZD", "GBPNZD" },
{ "GBP/USD", "GBPUSD" },
{ "GER30", "DE30EUR" },
{ "HKG33", "HK33HKD" },
{ "ITA40", "IT40EUR" },
{ "JPN225", "JP225JPY" },
{ "NAS100", "NAS100USD" },
{ "NGAS", "NATGASUSD" },
{ "NZD/CAD", "NZDCAD" },
{ "NZD/CHF", "NZDCHF" },
{ "NZD/JPY", "NZDJPY" },
{ "NZD/USD", "NZDUSD" },
{ "SPX500", "SPX500USD" },
{ "SUI20", "CH20CHF" },
{ "TRY/JPY", "TRYJPY" },
{ "UK100", "UK100GBP" },
{ "UKOil", "BCOUSD" },
{ "US30", "US30USD" },
{ "USD/MXN", "USDMXN" },
{ "USDOLLAR", "DXYUSD" },
{ "USD/CAD", "USDCAD" },
{ "USD/CHF", "USDCHF" },
{ "USD/CNH", "USDCNH" },
{ "USD/HKD", "USDHKD" },
{ "USD/JPY", "USDJPY" },
{ "USD/NOK", "USDNOK" },
{ "USD/SEK", "USDSEK" },
{ "USD/TRY", "USDTRY" },
{ "USD/ZAR", "USDZAR" },
{ "USOil", "WTICOUSD" },
{ "XAG/USD", "XAGUSD" },
{ "XAU/USD", "XAUUSD" },
{ "XPD/USD", "XPDUSD" },
{ "XPT/USD", "XPTUSD" },
{ "ZAR/JPY", "ZARJPY" }
};
private static readonly Dictionary<string, string> MapFxcmToLean = new Dictionary<string, string>();
private static readonly Dictionary<string, string> MapLeanToFxcm = new Dictionary<string, string>();
/// <summary>
/// The list of known FXCM currencies.
/// </summary>
private static readonly HashSet<string> KnownCurrencies = new HashSet<string>
{
"AUD", "CAD", "CHF", "CNH", "EUR", "GBP", "HKD", "JPY", "MXN", "NOK", "NZD", "SEK", "TRY", "USD", "ZAR"
};
/// <summary>
/// Static constructor for the <see cref="FxcmSymbolMapper"/> class
/// </summary>
static FxcmSymbolMapper()
{
foreach (var mapping in FxcmSymbolMappings)
{
MapFxcmToLean[mapping.Item1] = mapping.Item2;
MapLeanToFxcm[mapping.Item2] = mapping.Item1;
}
}
/// <summary>
/// Converts a Lean symbol instance to an FXCM symbol
/// </summary>
/// <param name="symbol">A Lean symbol instance</param>
/// <returns>The FXCM symbol</returns>
public string GetBrokerageSymbol(Symbol symbol)
{
if (symbol == null || string.IsNullOrWhiteSpace(symbol.Value))
throw new ArgumentException("Invalid symbol: " + (symbol == null ? "null" : symbol.ToString()));
if (symbol.ID.SecurityType != SecurityType.Forex && symbol.ID.SecurityType != SecurityType.Cfd)
throw new ArgumentException("Invalid security type: " + symbol.ID.SecurityType);
var brokerageSymbol = ConvertLeanSymbolToFxcmSymbol(symbol.Value);
if (!IsKnownBrokerageSymbol(brokerageSymbol))
throw new ArgumentException("Unknown symbol: " + symbol.Value);
return brokerageSymbol;
}
/// <summary>
/// Converts an FXCM symbol to a Lean symbol instance
/// </summary>
/// <param name="brokerageSymbol">The FXCM symbol</param>
/// <param name="securityType">The security type</param>
/// <param name="market">The market</param>
/// <param name="expirationDate">Expiration date of the security(if applicable)</param>
/// <param name="strike">The strike of the security (if applicable)</param>
/// <param name="optionRight">The option right of the security (if applicable)</param>
/// <returns>A new Lean Symbol instance</returns>
public Symbol GetLeanSymbol(string brokerageSymbol, SecurityType securityType, string market, DateTime expirationDate = default(DateTime), decimal strike = 0, OptionRight optionRight = 0)
{
if (string.IsNullOrWhiteSpace(brokerageSymbol))
throw new ArgumentException("Invalid FXCM symbol: " + brokerageSymbol);
if (!IsKnownBrokerageSymbol(brokerageSymbol))
throw new ArgumentException("Unknown FXCM symbol: " + brokerageSymbol);
if (securityType != SecurityType.Forex && securityType != SecurityType.Cfd)
throw new ArgumentException("Invalid security type: " + securityType);
if (market != Market.FXCM)
throw new ArgumentException("Invalid market: " + market);
return Symbol.Create(ConvertFxcmSymbolToLeanSymbol(brokerageSymbol), GetBrokerageSecurityType(brokerageSymbol), Market.FXCM);
}
/// <summary>
/// Returns the security type for an FXCM symbol
/// </summary>
/// <param name="brokerageSymbol">The FXCM symbol</param>
/// <returns>The security type</returns>
public SecurityType GetBrokerageSecurityType(string brokerageSymbol)
{
var tokens = brokerageSymbol.Split('/');
return tokens.Length == 2 && KnownCurrencies.Contains(tokens[0]) && KnownCurrencies.Contains(tokens[1])
? SecurityType.Forex
: SecurityType.Cfd;
}
/// <summary>
/// Returns the security type for a Lean symbol
/// </summary>
/// <param name="leanSymbol">The Lean symbol</param>
/// <returns>The security type</returns>
public SecurityType GetLeanSecurityType(string leanSymbol)
{
string fxcmSymbol;
if (!MapLeanToFxcm.TryGetValue(leanSymbol, out fxcmSymbol))
throw new ArgumentException("Unknown Lean symbol: " + leanSymbol);
return GetBrokerageSecurityType(fxcmSymbol);
}
/// <summary>
/// Checks if the symbol is supported by FXCM
/// </summary>
/// <param name="brokerageSymbol">The FXCM symbol</param>
/// <returns>True if FXCM supports the symbol</returns>
public bool IsKnownBrokerageSymbol(string brokerageSymbol)
{
return brokerageSymbol != null && MapFxcmToLean.ContainsKey(brokerageSymbol);
}
/// <summary>
/// Checks if the symbol is supported by FXCM
/// </summary>
/// <param name="symbol">The Lean symbol</param>
/// <returns>True if FXCM supports the symbol</returns>
public bool IsKnownLeanSymbol(Symbol symbol)
{
if (symbol == null || string.IsNullOrWhiteSpace(symbol.Value))
return false;
var fxcmSymbol = ConvertLeanSymbolToFxcmSymbol(symbol.Value);
return MapFxcmToLean.ContainsKey(fxcmSymbol) && GetBrokerageSecurityType(fxcmSymbol) == symbol.ID.SecurityType;
}
/// <summary>
/// Converts an FXCM symbol to a Lean symbol string
/// </summary>
public static string ConvertFxcmSymbolToLeanSymbol(string fxcmSymbol)
{
string leanSymbol;
return MapFxcmToLean.TryGetValue(fxcmSymbol, out leanSymbol) ? leanSymbol : string.Empty;
}
/// <summary>
/// Converts a Lean symbol string to an FXCM symbol
/// </summary>
private static string ConvertLeanSymbolToFxcmSymbol(string leanSymbol)
{
string fxcmSymbol;
return MapLeanToFxcm.TryGetValue(leanSymbol, out fxcmSymbol) ? fxcmSymbol : string.Empty;
}
}
}

View File

@@ -4,7 +4,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<RootNamespace>QuantConnect.Brokerages</RootNamespace>
<AssemblyName>QuantConnect.Brokerages</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>6</LangVersion>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<DefineConstants>NET45</DefineConstants>
@@ -32,7 +32,6 @@
</Target>
<ItemGroup>
<PackageReference Include="CsvHelper" Version="19.0.0" />
<PackageReference Include="IKVM" Version="8.1.5717.0" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="5.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -52,15 +51,6 @@
<Reference Include="CSharpAPI">
<HintPath>InteractiveBrokers\CSharpAPI.dll</HintPath>
</Reference>
<Reference Include="QuantConnect.Fxcm, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>Fxcm\QuantConnect.Fxcm.dll</HintPath>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Web" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
@@ -81,9 +71,4 @@
<ProjectReference Include="..\Configuration\QuantConnect.Configuration.csproj" />
<ProjectReference Include="..\Logging\QuantConnect.Logging.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="Fxcm\QuantConnect.Fxcm.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -13,8 +13,8 @@
* limitations under the License.
*/
using System.Linq;
using System.Collections.Generic;
using QuantConnect.Util;
namespace QuantConnect.Brokerages
{

View File

@@ -502,7 +502,17 @@ namespace QuantConnect.Data.Custom.TradingEconomics
/// <returns>ISO 4217 currency code</returns>
public static string CountryToCurrencyCode(string country)
{
var regions = CultureInfo.GetCultures(CultureTypes.SpecificCultures).Select(x => new RegionInfo(x.LCID));
var regions = CultureInfo.GetCultures(CultureTypes.SpecificCultures).Select(x =>
{
try
{
return new RegionInfo(x.LCID);
}
catch (Exception)
{
return null;
}
}).Where(info => info != null);
var countryLower = country.ToLowerInvariant();
return regions.FirstOrDefault(region => region.EnglishName.ToLowerInvariant().Contains(countryLower))?.ISOCurrencySymbol;
}

View File

@@ -18,7 +18,6 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Securities.Future;
using QuantConnect.Util;
namespace QuantConnect.Data.Market
{

View File

@@ -830,19 +830,6 @@ namespace QuantConnect
list.Add(tick);
}
/// <summary>
/// Extension method to round a double value to a fixed number of significant figures instead of a fixed decimal places.
/// </summary>
/// <param name="d">Double we're rounding</param>
/// <param name="digits">Number of significant figures</param>
/// <returns>New double rounded to digits-significant figures</returns>
public static double RoundToSignificantDigits(this double d, int digits)
{
if (d == 0) return 0;
var scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1);
return scale * Math.Round(d / scale, digits);
}
/// <summary>
/// Extension method to round a double value to a fixed number of significant figures instead of a fixed decimal places.
/// </summary>

View File

@@ -15,11 +15,11 @@
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Python.Runtime;
using QuantConnect.Logging;
using System.Collections.Generic;
using System.IO;
namespace QuantConnect.Python
{
@@ -46,6 +46,8 @@ namespace QuantConnect.Python
_isBeginAllowThreadsCalled = true;
Log.Trace("PythonInitializer.Initialize(): ended");
AddPythonPaths(new []{ Environment.CurrentDirectory });
}
}
@@ -58,7 +60,8 @@ namespace QuantConnect.Python
{
using (Py.GIL())
{
var code = string.Join(";", paths.Select(s => $"sys.path.append('{s}')"));
var code = string.Join(";", paths.Select(s => $"sys.path.append('{s}')"))
.Replace('\\', '/');
PythonEngine.Exec($"import sys;{code}");
}
}

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<ProductVersion>8.0.30703</ProductVersion>
<AssemblyName>QuantConnect.Common</AssemblyName>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
@@ -100,24 +100,6 @@
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0" />
<PackageReference Include="System.ServiceModel.Primitives" Version="4.7.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Configuration" />
<Reference Include="System.IdentityModel" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.IdentityModel" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Numerics" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Configuration\QuantConnect.Configuration.csproj" />
<ProjectReference Include="..\Logging\QuantConnect.Logging.csproj" />

View File

@@ -213,10 +213,13 @@ namespace QuantConnect.Securities
return null;
}
}
// if we've made it here we didn't find a security, so we'll need to add one
// Create a SecurityType to Market mapping with the markets from SecurityManager members
var markets = securities.Select(x => x.Key).GroupBy(x => x.SecurityType).ToDictionary(x => x.Key, y => y.First().ID.Market);
var markets = securities.Select(x => x.Key)
.GroupBy(x => x.SecurityType)
.ToDictionary(x => x.Key, y => y.Select(symbol => symbol.ID.Market).ToHashSet());
if (markets.ContainsKey(SecurityType.Cfd) && !markets.ContainsKey(SecurityType.Forex))
{
markets.Add(SecurityType.Forex, markets[SecurityType.Cfd]);
@@ -312,7 +315,7 @@ namespace QuantConnect.Securities
private static IEnumerable<KeyValuePair<SecurityDatabaseKey, SymbolProperties>> GetAvailableSymbolPropertiesDatabaseEntries(
SecurityType securityType,
IReadOnlyDictionary<SecurityType, string> marketMap,
IReadOnlyDictionary<SecurityType, string> markets
IReadOnlyDictionary<SecurityType, HashSet<string>> markets
)
{
var marketJoin = new HashSet<string>();
@@ -322,9 +325,13 @@ namespace QuantConnect.Securities
{
marketJoin.Add(market);
}
if (markets.TryGetValue(securityType, out market))
HashSet<string> existingMarkets;
if (markets.TryGetValue(securityType, out existingMarkets))
{
marketJoin.Add(market);
foreach (var existingMarket in existingMarkets)
{
marketJoin.Add(existingMarket);
}
}
}

View File

@@ -203,7 +203,7 @@ namespace QuantConnect.Securities.Option.StrategyMatcher
/// </summary>
/// <returns></returns>
public OptionPosition GetUnderlyingPosition()
=> LinqExtensions.GetValueOrDefault(_positions, Underlying, new OptionPosition(Underlying, 0));
=> _positions.GetValueOrDefault(Underlying, new OptionPosition(Underlying, 0));
/// <summary>
/// Creates a new <see cref="OptionPositionCollection"/> from the specified enumerable of <paramref name="positions"/>

View File

@@ -61,17 +61,6 @@ namespace QuantConnect.Util
return new ReadOnlyDictionary<K, V>(enumerable.ToDictionary());
}
/// <summary>
/// Creates a new <see cref="HashSet{T}"/> from the elements in the specified enumerable
/// </summary>
/// <typeparam name="T">The item type in the hash set</typeparam>
/// <param name="enumerable">The items to be placed into the enumerable</param>
/// <returns>A new <see cref="HashSet{T}"/> containing the items in the enumerable</returns>
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> enumerable)
{
return new HashSet<T>(enumerable);
}
/// <summary>
/// Creates a new <see cref="HashSet{T}"/> from the elements in the specified enumerable
/// </summary>
@@ -347,21 +336,6 @@ namespace QuantConnect.Util
}
}
/// <summary>
/// Gets the value associated with the specified key or provided default value if key is not found.
/// </summary>
/// <typeparam name="K">The key type</typeparam>
/// <typeparam name="V">The value type</typeparam>
/// <param name="dictionary">The dictionary instance</param>
/// <param name="key">Lookup key</param>
/// <param name="defaultValue">Default value</param>
/// <returns>Value associated with the specified key or default value</returns>
public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dictionary, K key, V defaultValue = default(V))
{
V obj;
return dictionary.TryGetValue(key, out obj) ? obj : defaultValue;
}
/// <summary>
/// Performs an action for each element in collection source
/// </summary>

View File

@@ -239,43 +239,46 @@ namespace QuantConnect.Util
List<Symbol> symbolsList;
Symbol symbol;
// Handle the possible types of conversions
if (PyList.IsListType(input))
using (Py.GIL())
{
List<string> symbolsStringList;
// Handle the possible types of conversions
if (PyList.IsListType(input))
{
List<string> symbolsStringList;
//Check if an entry in the list is a string type, if so then try and convert the whole list
if (PyString.IsStringType(input[0]) && input.TryConvert(out symbolsStringList))
{
symbolsList = new List<Symbol>();
foreach (var stringSymbol in symbolsStringList)
//Check if an entry in the list is a string type, if so then try and convert the whole list
if (PyString.IsStringType(input[0]) && input.TryConvert(out symbolsStringList))
{
symbol = QuantConnect.Symbol.Create(stringSymbol, SecurityType.Equity, Market.USA);
symbolsList.Add(symbol);
symbolsList = new List<Symbol>();
foreach (var stringSymbol in symbolsStringList)
{
symbol = QuantConnect.Symbol.Create(stringSymbol, SecurityType.Equity, Market.USA);
symbolsList.Add(symbol);
}
}
//Try converting it to list of symbols, if it fails throw exception
else if (!input.TryConvert(out symbolsList))
{
throw new ArgumentException($"Cannot convert list {input.Repr()} to symbols");
}
}
//Try converting it to list of symbols, if it fails throw exception
else if (!input.TryConvert(out symbolsList))
{
throw new ArgumentException($"Cannot convert list {input.Repr()} to symbols");
}
}
else
{
//Check if its a single string, and try and convert it
string symbolString;
if (PyString.IsStringType(input) && input.TryConvert(out symbolString))
{
symbol = QuantConnect.Symbol.Create(symbolString, SecurityType.Equity, Market.USA);
symbolsList = new List<Symbol> { symbol };
}
else if (input.TryConvert(out symbol))
{
symbolsList = new List<Symbol> { symbol };
}
else
{
throw new ArgumentException($"Cannot convert object {input.Repr()} to symbol");
//Check if its a single string, and try and convert it
string symbolString;
if (PyString.IsStringType(input) && input.TryConvert(out symbolString))
{
symbol = QuantConnect.Symbol.Create(symbolString, SecurityType.Equity, Market.USA);
symbolsList = new List<Symbol> { symbol };
}
else if (input.TryConvert(out symbol))
{
symbolsList = new List<Symbol> { symbol };
}
else
{
throw new ArgumentException($"Cannot convert object {input.Repr()} to symbol");
}
}
}
return symbolsList;

View File

@@ -115,27 +115,34 @@ namespace QuantConnect.Util
// for providing the code below, fixing issue #3499 - https://github.com/QuantConnect/Lean/issues/3499
private void ExitTimerCallback(object state)
{
// While there are exit times that are passed due still in the queue,
// exit the semaphore and dequeue the exit time.
var exitTime = 0;
var exitTimeValid = _exitTimes.TryPeek(out exitTime);
while (exitTimeValid)
try
{
if (unchecked(exitTime - Environment.TickCount) > 0)
// While there are exit times that are passed due still in the queue,
// exit the semaphore and dequeue the exit time.
var exitTime = 0;
var exitTimeValid = _exitTimes.TryPeek(out exitTime);
while (exitTimeValid)
{
break;
if (unchecked(exitTime - Environment.TickCount) > 0)
{
break;
}
_semaphore.Release();
_exitTimes.TryDequeue(out exitTime);
exitTimeValid = _exitTimes.TryPeek(out exitTime);
}
_semaphore.Release();
_exitTimes.TryDequeue(out exitTime);
exitTimeValid = _exitTimes.TryPeek(out exitTime);
}
// we are already holding the next item from the queue, do not peek again
// although this exit time may have already pass by this stmt.
var timeUntilNextCheck = exitTimeValid
? Math.Min(TimeUnitMilliseconds, Math.Max(0, exitTime - Environment.TickCount))
: TimeUnitMilliseconds;
// we are already holding the next item from the queue, do not peek again
// although this exit time may have already pass by this stmt.
var timeUntilNextCheck = exitTimeValid
? Math.Min(TimeUnitMilliseconds, Math.Max(0, exitTime - Environment.TickCount))
: TimeUnitMilliseconds;
_exitTimer.Change(timeUntilNextCheck, -1);
_exitTimer.Change(timeUntilNextCheck, -1);
}
catch (Exception)
{
// can throw if called when disposing
}
}
/// <summary>

View File

@@ -4,7 +4,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<RootNamespace>QuantConnect.Compression</RootNamespace>
<AssemblyName>QuantConnect.Compression</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>6</LangVersion>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
@@ -37,11 +37,6 @@
</PackageReference>
<PackageReference Include="SharpZipLib" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.IO.Compression" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -4,7 +4,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<RootNamespace>QuantConnect.Configuration</RootNamespace>
<AssemblyName>QuantConnect.Configuration</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<LangVersion>6</LangVersion>
@@ -38,10 +38,6 @@
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -14,6 +14,9 @@ RUN pip install ptvsd
#Install PyDev Debugger for Pycharm for remote python debugging
RUN pip install pydevd-pycharm~=201.8538.36
# Install vsdbg for remote C# debugging in Visual Studio and Visual Studio Code
RUN wget https://aka.ms/getvsdbgsh -O - 2>/dev/null | /bin/sh /dev/stdin -v 16.9.20122.2 -l /root/vsdbg
COPY ./Launcher/bin/Debug/ /Lean/Launcher/bin/Debug/
COPY ./Optimizer.Launcher/bin/Debug/ /Lean/Optimizer.Launcher/bin/Debug/
COPY ./Report/bin/Debug/ /Lean/Report/bin/Debug/
@@ -21,4 +24,4 @@ COPY ./Report/bin/Debug/ /Lean/Report/bin/Debug/
# Can override with '-w'
WORKDIR /Lean/Launcher/bin/Debug
ENTRYPOINT [ "mono", "QuantConnect.Lean.Launcher.exe" ]
ENTRYPOINT [ "dotnet", "QuantConnect.Lean.Launcher.dll" ]

View File

@@ -14,24 +14,25 @@ RUN wget --quiet https://github.com/krallin/tini/releases/download/v0.10.0/tini
mv tini /usr/local/bin/tini && \
chmod +x /usr/local/bin/tini
# Clone Lean; Copy Python startup file to profile; Install Lean/PythonToolbox; Remove extra files
# Clone Lean; Link Python startup file to profile; Install Lean/PythonToolbox; Remove extra files
RUN git clone https://github.com/QuantConnect/Lean.git && \
mkdir -p /root/.ipython/profile_default/startup/ && cp -f Lean/Research/start.py /root/.ipython/profile_default/startup/ && \
mkdir -p /root/.ipython/profile_default/startup/ && \
ln -s /Lean/Launcher/bin/Debug/start.py /root/.ipython/profile_default/startup/start.py && \
cd Lean/PythonToolbox && python setup.py install \
&& cd ../.. && rm -irf Lean
RUN conda install -y -c conda-forge notebook=6.0.3
#Install ICSharp (Jupyter C# Kernel)
RUN wget https://cdn.quantconnect.com/icsharp/ICSharp.Kernel.20180820.zip && \
unzip ICSharp.Kernel.20180820.zip -d / && rm -irf ICSharp.Kernel.20180820.zip && cd /icsharp && \
jupyter kernelspec install kernel-spec --name=csharp
# Install .NET Interactive to support C# in Jupyter notebooks
ENV PATH="${PATH}:/root/.dotnet/tools"
RUN dotnet tool install -g --add-source "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" Microsoft.dotnet-interactive && \
dotnet interactive jupyter install
# Setting some environment variables
ENV WORK /Lean/Launcher/bin/Debug/
ENV PYTHONPATH=${WORK}:${PYTHONPATH}
RUN find ${WORK} -type f -not -name '*.py*' -not -name '*.xml' -not -name '*.exe.config' -not -name '*.exe' -not -name '*.so' -not -name '*.dll' -not -name '*.ipynb' -not -name '*.csx' -not -name 'decimal.py' -delete
RUN find ${WORK} -type f -not -name '*.py*' -not -name '*.xml' -not -name '*.exe.config' -not -name '*.exe' -not -name '*.so' -not -name '*.dll' -not -name '*.ipynb' -not -name '*.csx' -not -name 'QuantConnect.Lean.Launcher.runtimeconfig.json' -not -name 'decimal.py' -delete
#Create initialize script
RUN echo "if [ ! -d \"${WORK}Notebooks\" ]; then mkdir ${WORK}Notebooks; fi && \

View File

@@ -31,14 +31,6 @@ RUN wget https://cdn.quantconnect.com/interactive/ibgateway-stable-standalone-li
wget -O ~/Jts/jts.ini https://cdn.quantconnect.com/interactive/ibgateway-latest-standalone-linux-x64-v974.4g.jts.ini && \
rm ibgateway-stable-standalone-linux-x64-v978.2c.sh
# Mono C# for LEAN:
# From https://github.com/mono/docker/blob/master/
RUN apt-get update && rm -rf /var/lib/apt/lists/*
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
RUN echo "deb http://download.mono-project.com/repo/ubuntu stable-xenial/snapshots/5.12.0.226 main" > /etc/apt/sources.list.d/mono-xamarin.list && \
apt-get update && apt-fast install -y binutils mono-complete ca-certificates-mono mono-vbnc nuget referenceassemblies-pcl && \
apt-fast install -y fsharp && rm -rf /var/lib/apt/lists/* /tmp/*
# Install dotnet 5 sdk & runtime
RUN wget https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb && \
dpkg -i packages-microsoft-prod.deb && \

View File

@@ -5,7 +5,7 @@
<ProductVersion>8.0.30703</ProductVersion>
<RootNamespace>QuantConnect.Lean.Engine</RootNamespace>
<AssemblyName>QuantConnect.Lean.Engine</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<PublishUrl>publish\</PublishUrl>
@@ -108,13 +108,6 @@
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Runtime.Caching" />
<Reference Include="System.Web" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -4,7 +4,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<RootNamespace>QuantConnect.Indicators</RootNamespace>
<AssemblyName>QuantConnect.Indicators</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>6</LangVersion>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
@@ -49,10 +49,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -5,7 +5,7 @@
<OutputType>Exe</OutputType>
<RootNamespace>QuantConnect.Lean.Launcher</RootNamespace>
<AssemblyName>QuantConnect.Lean.Launcher</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>6</LangVersion>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
@@ -37,7 +37,6 @@
</Target>
<ItemGroup>
<PackageReference Include="DynamicInterop" Version="0.9.1" />
<PackageReference Include="IKVM" Version="8.1.5717.0" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="5.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -45,11 +44,6 @@
<PackageReference Include="R.NET" Version="1.9.0" />
<PackageReference Include="System.ComponentModel.Composition" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -5,7 +5,7 @@
<ProductVersion>8.0.30703</ProductVersion>
<RootNamespace>QuantConnect.Logging</RootNamespace>
<AssemblyName>QuantConnect.Logging</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<LangVersion>6</LangVersion>
@@ -57,10 +57,6 @@
</PackageReference>
<PackageReference Include="System.ComponentModel.Composition" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -4,7 +4,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<RootNamespace>QuantConnect.Messaging</RootNamespace>
<AssemblyName>QuantConnect.Messaging</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>6</LangVersion>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
@@ -40,12 +40,6 @@
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="RestSharp" Version="106.6.10" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.ServiceModel" />
<Reference Include="System.Web" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -47,7 +47,7 @@ namespace QuantConnect.Optimizer.Launcher
Directory.CreateDirectory(_rootResultDirectory);
_leanLocation = Configuration.Config.Get("lean-binaries-location",
Path.Combine(Directory.GetCurrentDirectory(), "../../../Launcher/bin/Debug/QuantConnect.Lean.Launcher.exe"));
Path.Combine(Directory.GetCurrentDirectory(), "../../../Launcher/bin/Debug/QuantConnect.Lean.Launcher"));
var closeLeanAutomatically = Configuration.Config.GetBool("optimizer-close-automatically", true);
_extraLeanArguments = $"--close-automatically {closeLeanAutomatically}";

View File

@@ -5,7 +5,7 @@
<OutputType>Exe</OutputType>
<RootNamespace>QuantConnect.Optimizer.Launcher</RootNamespace>
<AssemblyName>QuantConnect.Optimizer.Launcher</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<OutputPath>bin\$(Configuration)\</OutputPath>
@@ -38,11 +38,6 @@
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -4,7 +4,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<RootNamespace>QuantConnect.Optimizer</RootNamespace>
<AssemblyName>QuantConnect.Optimizer</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<LangVersion>6</LangVersion>
@@ -40,11 +40,6 @@
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -4,7 +4,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<RootNamespace>QuantConnect.Queues</RootNamespace>
<AssemblyName>QuantConnect.Queues</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>6</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<OutputPath>bin\$(Configuration)\</OutputPath>
@@ -36,10 +36,6 @@
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -31,9 +31,6 @@ namespace QuantConnect.Report
{
static void Main(string[] args)
{
// Adds the current working directory to the PYTHONPATH env var.
PythonInitializer.SetPythonPathEnvironmentVariable();
// Parse report arguments and merge with config to use in report creator:
if (args.Length > 0)
{
@@ -89,8 +86,13 @@ namespace QuantConnect.Report
{
Console.Write(html);
}
Log.Trace("QuantConnect.Report.Main(): Completed.");
Console.ReadKey();
if (!Console.IsInputRedirected)
{
Console.ReadKey();
}
}
}
}

View File

@@ -5,7 +5,7 @@
<OutputType>Exe</OutputType>
<RootNamespace>QuantConnect.Report</RootNamespace>
<AssemblyName>QuantConnect.Report</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<OutputPath>bin\$(Configuration)\</OutputPath>
@@ -67,10 +67,6 @@
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>

View File

@@ -4,7 +4,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<RootNamespace>QuantConnect.Research</RootNamespace>
<AssemblyName>QuantConnect.Research</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>6</LangVersion>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<OutputPath>bin\$(Configuration)\</OutputPath>
@@ -18,7 +18,7 @@
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<PlatformTarget>x64</PlatformTarget>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -52,10 +52,6 @@
<PackageReference Include="NodaTime" Version="3.0.5" />
<PackageReference Include="System.ComponentModel.Composition" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Algorithm.Framework\QuantConnect.Algorithm.Framework.csproj" />
<ProjectReference Include="..\Algorithm\QuantConnect.Algorithm.csproj" />

View File

@@ -89,6 +89,10 @@ and [Python installation](https://github.com/QuantConnect/Lean/tree/master/Algor
2. Install [QuantConnect Python API](https://pypi.python.org/pypi/quantconnect/0.1)
```
pip install quantconnect
```
3. Install [pythonnet/clr-loader](https://github.com/pythonnet/clr-loader)
```
pip install clr-loader
```
**2. Run Jupyter:**
1. Update the `config.json` file in `Lean/Launcher/bin/Debug/` folder

View File

@@ -17,6 +17,16 @@
# Usage:
# %run "start.py"
import clr_loader
import os
from pythonnet import set_runtime
# The runtimeconfig.json is stored alongside start.py, but start.py may be a
# symlink and the directory start.py is stored in is not necessarily the
# current working directory. We therefore construct the absolute path to the
# start.py file, and find the runtimeconfig.json relative to that.
set_runtime(clr_loader.get_coreclr(os.path.join(os.path.dirname(os.path.realpath(__file__)), "QuantConnect.Lean.Launcher.runtimeconfig.json")))
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
@@ -39,4 +49,4 @@ from QuantConnect.Indicators import *
api = Api()
api.Initialize(Config.GetInt("job-user-id", 1),
Config.Get("api-access-token", "default"),
Config.Get("data-folder"))
Config.Get("data-folder"))

View File

@@ -24,7 +24,7 @@ using QuantConnect.Securities.Future;
using QuantConnect.Securities.Option;
using QuantConnect.Tests.Engine.DataFeeds;
using System;
using QuantConnect.Securities.Index;
using Index = QuantConnect.Securities.Index.Index;
namespace QuantConnect.Tests.Algorithm
{

View File

@@ -194,8 +194,8 @@ class Test(AlphaStreamsBrokerageModel):
var oandaSecurity = _algo.AddSecurity(SecurityType.Forex, "EURUSD", Resolution.Minute, Market.Oanda, true, 1m, true);
Assert.AreEqual(2, _algo.Securities.Count);
Assert.AreEqual(Market.FXCM, _algo.Securities.First().Key.ID.Market);
Assert.AreEqual(Market.Oanda, _algo.Securities.Last().Key.ID.Market);
Assert.IsNotNull(_algo.Securities.Single(pair => pair.Key.ID.Market == Market.FXCM));
Assert.IsNotNull(_algo.Securities.Single(pair => pair.Key.ID.Market == Market.Oanda));
Assert.AreEqual(Market.FXCM, fxcmSecurity.Symbol.ID.Market);
Assert.AreEqual(Market.Oanda, oandaSecurity.Symbol.ID.Market);
}
@@ -207,8 +207,8 @@ class Test(AlphaStreamsBrokerageModel):
var oandaSecurity = _algo.AddForex("EURUSD", Resolution.Minute, Market.Oanda);
Assert.AreEqual(2, _algo.Securities.Count);
Assert.AreEqual(Market.FXCM, _algo.Securities.First().Key.ID.Market);
Assert.AreEqual(Market.Oanda, _algo.Securities.Last().Key.ID.Market);
Assert.IsNotNull(_algo.Securities.Single(pair => pair.Key.ID.Market == Market.FXCM));
Assert.IsNotNull(_algo.Securities.Single(pair => pair.Key.ID.Market == Market.Oanda));
Assert.AreEqual(Market.FXCM, fxcmSecurity.Symbol.ID.Market);
Assert.AreEqual(Market.Oanda, oandaSecurity.Symbol.ID.Market);
}

View File

@@ -159,8 +159,15 @@ namespace QuantConnect.Tests
foreach (var stat in expectedStatistics)
{
Assert.AreEqual(true, statistics.ContainsKey(stat.Key), "Missing key: " + stat.Key);
Assert.AreEqual(stat.Value, statistics[stat.Key], "Failed on " + stat.Key);
string result;
Assert.IsTrue(statistics.TryGetValue(stat.Key, out result), "Missing key: " + stat.Key);
if (result == "-0")
{
// normalize -0 & 0, they are the same thing
result = "0";
}
Assert.AreEqual(stat.Value, result, "Failed on " + stat.Key);
}
if (expectedAlphaStatistics != null)

View File

@@ -44,7 +44,8 @@ namespace QuantConnect.Tests
Directory.SetCurrentDirectory(dir);
Config.Reset();
Globals.Reset();
PythonInitializer.SetPythonPathEnvironmentVariable(
PythonInitializer.Initialize();
PythonInitializer.AddPythonPaths(
new[]
{
"./Alphas",

View File

@@ -1,66 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using QuantConnect.Brokerages.Fxcm;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Logging;
namespace QuantConnect.Tests.Brokerages.Fxcm
{
[TestFixture]
public partial class FxcmBrokerageTests
{
[Test]
public void GetsTickData()
{
var brokerage = (FxcmBrokerage)Brokerage;
var cancelationToken = new CancellationTokenSource();
var configs = new SubscriptionDataConfig[] {
GetSubscriptionDataConfig<TradeBar>(Symbols.USDJPY, Resolution.Second),
GetSubscriptionDataConfig<TradeBar>(Symbols.EURGBP, Resolution.Second)
};
foreach (var config in configs)
{
ProcessFeed(
brokerage.Subscribe(config, (s, e) => { }),
cancelationToken,
(tick) => {
if (tick != null)
{
Log.Trace("{0}: {1} - {2} / {3}", tick.Time.ToStringInvariant("yyyy-MM-dd HH:mm:ss.fff"), tick.Symbol, (tick as Tick)?.BidPrice, (tick as Tick)?.AskPrice);
}
});
}
Thread.Sleep(5000);
foreach (var config in configs)
{
brokerage.Unsubscribe(config);
}
Thread.Sleep(20000);
cancelationToken.Cancel();
}
}
}

View File

@@ -1,165 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Diagnostics;
using System.Linq;
using NodaTime;
using NUnit.Framework;
using QuantConnect.Brokerages.Fxcm;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Lean.Engine.HistoricalData;
using QuantConnect.Logging;
using QuantConnect.Securities;
namespace QuantConnect.Tests.Brokerages.Fxcm
{
[TestFixture]
public partial class FxcmBrokerageTests
{
private static TestCaseData[] TestParameters
{
get
{
return new[]
{
// valid parameters
new TestCaseData(Symbols.EURUSD, Resolution.Tick, TimeSpan.FromSeconds(15), false),
new TestCaseData(Symbols.EURUSD, Resolution.Second, Time.OneMinute, false),
new TestCaseData(Symbols.EURUSD, Resolution.Minute, Time.OneHour, false),
new TestCaseData(Symbols.EURUSD, Resolution.Hour, Time.OneDay, false),
new TestCaseData(Symbols.EURUSD, Resolution.Daily, TimeSpan.FromDays(15), false),
// invalid period, no error, empty result
new TestCaseData(Symbols.EURUSD, Resolution.Daily, TimeSpan.FromDays(-15), false),
// invalid symbol, throws "System.ArgumentException : Unknown symbol: XYZ"
new TestCaseData(Symbol.Create("XYZ", SecurityType.Forex, Market.FXCM), Resolution.Daily, TimeSpan.FromDays(15), true),
// invalid security type, throws "System.ArgumentException : Invalid security type: Equity"
new TestCaseData(Symbols.AAPL, Resolution.Daily, TimeSpan.FromDays(15), true),
};
}
}
[Test, TestCaseSource(nameof(TestParameters))]
public void GetsHistory(Symbol symbol, Resolution resolution, TimeSpan period, bool throwsException)
{
TestDelegate test = () =>
{
var brokerage = (FxcmBrokerage) Brokerage;
var historyProvider = new BrokerageHistoryProvider();
historyProvider.SetBrokerage(brokerage);
historyProvider.Initialize(new HistoryProviderInitializeParameters(null, null, null, null, null, null, null, false, null));
var now = DateTime.UtcNow;
var requests = new[]
{
new HistoryRequest(now.Add(-period),
now,
typeof(QuoteBar),
symbol,
resolution,
SecurityExchangeHours.AlwaysOpen(TimeZones.Utc),
DateTimeZone.Utc,
Resolution.Minute,
false,
false,
DataNormalizationMode.Adjusted,
TickType.Quote)
};
var history = historyProvider.GetHistory(requests, TimeZones.Utc);
foreach (var slice in history)
{
if (resolution == Resolution.Tick)
{
foreach (var tick in slice.Ticks[symbol])
{
Log.Trace("{0}: {1} - {2} / {3}", tick.Time.ToStringInvariant("yyyy-MM-dd HH:mm:ss.fff"), tick.Symbol, tick.BidPrice, tick.AskPrice);
}
}
else
{
var bar = slice.Bars[symbol];
Log.Trace("{0}: {1} - O={2}, H={3}, L={4}, C={5}", bar.Time, bar.Symbol, bar.Open, bar.High, bar.Low, bar.Close);
}
}
Log.Trace("Data points retrieved: " + historyProvider.DataPointCount);
};
if (throwsException)
{
Assert.Throws<ArgumentException>(test);
}
else
{
Assert.DoesNotThrow(test);
}
}
[Test]
public void GetsFullDayTickHistory()
{
var symbol = Symbols.EURUSD;
const Resolution resolution = Resolution.Tick;
var startDate = new DateTime(2016, 11, 1);
var endDate = startDate.AddDays(1);
var brokerage = (FxcmBrokerage)Brokerage;
var historyProvider = new BrokerageHistoryProvider();
historyProvider.SetBrokerage(brokerage);
historyProvider.Initialize(new HistoryProviderInitializeParameters(null, null, null, null, null, null, null, false, null));
var stopwatch = Stopwatch.StartNew();
var requests = new[]
{
new HistoryRequest(startDate,
endDate,
typeof(QuoteBar),
symbol,
resolution,
SecurityExchangeHours.AlwaysOpen(TimeZones.Utc),
DateTimeZone.Utc,
Resolution.Minute,
false,
false,
DataNormalizationMode.Adjusted,
TickType.Quote)
};
var history = historyProvider.GetHistory(requests, TimeZones.Utc);
var tickCount = history.SelectMany(slice => slice.Ticks[symbol]).Count();
stopwatch.Stop();
Log.Trace("Download time: " + stopwatch.Elapsed);
Log.Trace("History ticks returned: " + tickCount);
Log.Trace("Data points retrieved: " + historyProvider.DataPointCount);
Assert.AreEqual(tickCount, historyProvider.DataPointCount);
Assert.AreEqual(241233, tickCount);
}
}
}

View File

@@ -1,226 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using NUnit.Framework;
using QuantConnect.Brokerages.Fxcm;
using QuantConnect.Configuration;
using QuantConnect.Interfaces;
using QuantConnect.Lean.Engine.DataFeeds;
using QuantConnect.Logging;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace QuantConnect.Tests.Brokerages.Fxcm
{
[TestFixture, Ignore("These tests require a configured and active FXCM practice account")]
public partial class FxcmBrokerageTests : BrokerageTests
{
/// <summary>
/// Creates the brokerage under test
/// </summary>
/// <returns>A connected brokerage instance</returns>
protected override IBrokerage CreateBrokerage(IOrderProvider orderProvider, ISecurityProvider securityProvider)
{
var server = Config.Get("fxcm-server");
var terminal = Config.Get("fxcm-terminal");
var userName = Config.Get("fxcm-user-name");
var password = Config.Get("fxcm-password");
var accountId = Config.Get("fxcm-account-id");
return new FxcmBrokerage(orderProvider, securityProvider, new AggregationManager(), server, terminal, userName, password, accountId);
}
/// <summary>
/// Disposes of the brokerage and any external resources started in order to create it
/// </summary>
/// <param name="brokerage">The brokerage instance to be disposed of</param>
protected override void DisposeBrokerage(IBrokerage brokerage)
{
brokerage.Disconnect();
}
/// <summary>
/// Provides the data required to test each order type in various cases
/// </summary>
public static TestCaseData[] OrderParameters
{
get
{
return new[]
{
new TestCaseData(new MarketOrderTestParameters(Symbols.EURUSD)).SetName("MarketOrder"),
new TestCaseData(new FxcmLimitOrderTestParameters(Symbols.EURUSD, 1.5m, 0.7m)).SetName("LimitOrder"),
new TestCaseData(new FxcmStopMarketOrderTestParameters(Symbols.EURUSD, 1.5m, 0.7m)).SetName("StopMarketOrder"),
};
}
}
/// <summary>
/// Gets the symbol to be traded, must be shortable
/// </summary>
protected override Symbol Symbol => Symbols.EURUSD;
/// <summary>
/// Gets the security type associated with the <see cref="BrokerageTests.Symbol"/>
/// </summary>
protected override SecurityType SecurityType => SecurityType.Forex;
/// <summary>
/// Returns wether or not the brokers order methods implementation are async
/// </summary>
protected override bool IsAsync()
{
return false;
}
/// <summary>
/// Gets the current market price of the specified security
/// </summary>
protected override decimal GetAskPrice(Symbol symbol)
{
// not used, we use bid/ask prices
return 0;
}
/// <summary>
/// Gets the default order quantity
/// </summary>
protected override decimal GetDefaultQuantity()
{
// FXCM requires a multiple of 1000 for Forex instruments
return 1000;
}
[Test, Ignore("This test requires disconnecting the internet to test for connection resiliency")]
public void ClientReconnectsAfterInternetDisconnect()
{
var brokerage = Brokerage;
Assert.IsTrue(brokerage.IsConnected);
var tenMinutes = TimeSpan.FromMinutes(10);
Log.Trace("------");
Log.Trace("Waiting for internet disconnection ");
Log.Trace("------");
// spin while we manually disconnect the internet
while (brokerage.IsConnected)
{
Thread.Sleep(2500);
Console.Write(".");
}
var stopwatch = Stopwatch.StartNew();
Log.Trace("------");
Log.Trace("Trying to reconnect ");
Log.Trace("------");
// spin until we're reconnected
while (!brokerage.IsConnected && stopwatch.Elapsed < tenMinutes)
{
Thread.Sleep(2500);
Console.Write(".");
}
Assert.IsTrue(brokerage.IsConnected);
}
[TestCase("EURGBP", SecurityType.Forex, Market.FXCM, 50000)]
[TestCase("EURGBP", SecurityType.Forex, Market.FXCM, -50000)]
[TestCase("DE30EUR", SecurityType.Cfd, Market.FXCM, 10)]
[TestCase("DE30EUR", SecurityType.Cfd, Market.FXCM, -10)]
public void GetCashBalanceIncludesCurrencySwapsForOpenPositions(string ticker, SecurityType securityType, string market, decimal quantity)
{
// This test requires a practice account with USD account currency
var brokerage = Brokerage;
Assert.IsTrue(brokerage.IsConnected);
var symbol = Symbol.Create(ticker, securityType, market);
var order = new MarketOrder(symbol, quantity, DateTime.UtcNow);
PlaceOrderWaitForStatus(order);
var holdings = brokerage.GetAccountHoldings();
var balances = brokerage.GetCashBalance();
Assert.IsTrue(holdings.Count == 1);
// account currency
Assert.IsTrue(balances.Any(x => x.Currency == "USD"));
if (securityType == SecurityType.Forex)
{
// base currency
var baseCurrencyCash = balances.Single(x => x.Currency == ticker.Substring(0, 3));
Assert.AreEqual(quantity, baseCurrencyCash.Amount);
// quote currency
var quoteCurrencyCash = balances.Single(x => x.Currency == ticker.Substring(3));
Assert.AreEqual(-Math.Sign(quantity), Math.Sign(quoteCurrencyCash.Amount));
}
else if (securityType == SecurityType.Cfd)
{
Assert.AreEqual(1, balances.Count);
}
}
[Test, TestCaseSource(nameof(OrderParameters))]
public override void CancelOrders(OrderTestParameters parameters)
{
base.CancelOrders(parameters);
}
[Test, TestCaseSource(nameof(OrderParameters))]
public override void LongFromZero(OrderTestParameters parameters)
{
base.LongFromZero(parameters);
}
[Test, TestCaseSource(nameof(OrderParameters))]
public override void CloseFromLong(OrderTestParameters parameters)
{
base.CloseFromLong(parameters);
}
[Test, TestCaseSource(nameof(OrderParameters))]
public override void ShortFromZero(OrderTestParameters parameters)
{
base.ShortFromZero(parameters);
}
[Test, TestCaseSource(nameof(OrderParameters))]
public override void CloseFromShort(OrderTestParameters parameters)
{
base.CloseFromShort(parameters);
}
[Test, TestCaseSource(nameof(OrderParameters))]
public override void ShortFromLong(OrderTestParameters parameters)
{
base.ShortFromLong(parameters);
}
[Test, TestCaseSource(nameof(OrderParameters))]
public override void LongFromShort(OrderTestParameters parameters)
{
base.LongFromShort(parameters);
}
}
}

View File

@@ -1,64 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Brokerages.Fxcm;
using QuantConnect.Interfaces;
using QuantConnect.Logging;
using QuantConnect.Orders;
namespace QuantConnect.Tests.Brokerages.Fxcm
{
public class FxcmLimitOrderTestParameters : LimitOrderTestParameters
{
public FxcmLimitOrderTestParameters(Symbol symbol, decimal highLimit, decimal lowLimit)
: base(symbol, highLimit, lowLimit)
{
}
public override bool ModifyOrderToFill(IBrokerage brokerage, Order order, decimal lastMarketPrice)
{
// FXCM Buy Limit orders will be rejected if the limit price is above the market price
// FXCM Sell Limit orders will be rejected if the limit price is below the market price
var limit = (LimitOrder)order;
var previousLimit = limit.LimitPrice;
var fxcmBrokerage = (FxcmBrokerage)brokerage;
var quotes = fxcmBrokerage.GetBidAndAsk(new List<string> { new FxcmSymbolMapper().GetBrokerageSymbol(order.Symbol) });
if (order.Quantity > 0)
{
// for limit buys we need to increase the limit price
// buy limit price must be at bid price or below
var bidPrice = Convert.ToDecimal(quotes.Single().BidPrice);
Log.Trace("FxcmLimitOrderTestParameters.ModifyOrderToFill(): Bid: " + bidPrice);
limit.LimitPrice = Math.Max(previousLimit, Math.Min(bidPrice, limit.LimitPrice * 2));
}
else
{
// for limit sells we need to decrease the limit price
// sell limit price must be at ask price or above
var askPrice = Convert.ToDecimal(quotes.Single().AskPrice);
Log.Trace("FxcmLimitOrderTestParameters.ModifyOrderToFill(): Ask: " + askPrice);
limit.LimitPrice = Math.Min(previousLimit, Math.Max(askPrice, limit.LimitPrice / 2));
}
return limit.LimitPrice != previousLimit;
}
}
}

View File

@@ -1,64 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Brokerages.Fxcm;
using QuantConnect.Interfaces;
using QuantConnect.Logging;
using QuantConnect.Orders;
namespace QuantConnect.Tests.Brokerages.Fxcm
{
public class FxcmStopMarketOrderTestParameters : StopMarketOrderTestParameters
{
public FxcmStopMarketOrderTestParameters(Symbol symbol, decimal highLimit, decimal lowLimit)
: base(symbol, highLimit, lowLimit)
{
}
public override bool ModifyOrderToFill(IBrokerage brokerage, Order order, decimal lastMarketPrice)
{
// FXCM Buy StopMarket orders will be rejected if the stop price is below the market price
// FXCM Sell StopMarket orders will be rejected if the stop price is above the market price
var stop = (StopMarketOrder)order;
var previousStop = stop.StopPrice;
var fxcmBrokerage = (FxcmBrokerage)brokerage;
var quotes = fxcmBrokerage.GetBidAndAsk(new List<string> { new FxcmSymbolMapper().GetBrokerageSymbol(order.Symbol) });
if (order.Quantity > 0)
{
// for stop buys we need to decrease the stop price
// buy stop price must be strictly above ask price
var askPrice = Convert.ToDecimal(quotes.Single().AskPrice);
Log.Trace("FxcmStopMarketOrderTestParameters.ModifyOrderToFill(): Ask: " + askPrice);
stop.StopPrice = Math.Min(previousStop, Math.Max(askPrice, stop.StopPrice / 2) + 0.00001m);
}
else
{
// for stop sells we need to increase the stop price
// sell stop price must be strictly below bid price
var bidPrice = Convert.ToDecimal(quotes.Single().BidPrice);
Log.Trace("FxcmStopMarketOrderTestParameters.ModifyOrderToFill(): Bid: " + bidPrice);
stop.StopPrice = Math.Max(previousStop, Math.Min(bidPrice, stop.StopPrice * 2) - 0.00001m);
}
return stop.StopPrice != previousStop;
}
}
}

View File

@@ -1,120 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using NUnit.Framework;
using QuantConnect.Brokerages.Fxcm;
namespace QuantConnect.Tests.Brokerages.Fxcm
{
[TestFixture]
public class FxcmSymbolMapperTests
{
[Test]
public void ReturnsCorrectLeanSymbol()
{
var mapper = new FxcmSymbolMapper();
var symbol = mapper.GetLeanSymbol("EUR/USD", SecurityType.Forex, Market.FXCM);
Assert.AreEqual("EURUSD", symbol.Value);
Assert.AreEqual(SecurityType.Forex, symbol.ID.SecurityType);
Assert.AreEqual(Market.FXCM, symbol.ID.Market);
symbol = mapper.GetLeanSymbol("GER30", SecurityType.Cfd, Market.FXCM);
Assert.AreEqual("DE30EUR", symbol.Value);
Assert.AreEqual(SecurityType.Cfd, symbol.ID.SecurityType);
Assert.AreEqual(Market.FXCM, symbol.ID.Market);
}
[Test]
public void ReturnsCorrectBrokerageSymbol()
{
var mapper = new FxcmSymbolMapper();
var symbol = Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM);
var brokerageSymbol = mapper.GetBrokerageSymbol(symbol);
Assert.AreEqual("EUR/USD", brokerageSymbol);
symbol = Symbol.Create("DE30EUR", SecurityType.Cfd, Market.FXCM);
brokerageSymbol = mapper.GetBrokerageSymbol(symbol);
Assert.AreEqual("GER30", brokerageSymbol);
}
[Test]
public void ThrowsOnNullOrEmptySymbol()
{
var mapper = new FxcmSymbolMapper();
Assert.Throws<ArgumentException>(() => mapper.GetLeanSymbol(null, SecurityType.Forex, Market.FXCM));
Assert.Throws<ArgumentException>(() => mapper.GetLeanSymbol("", SecurityType.Forex, Market.FXCM));
var symbol = Symbol.Empty;
Assert.Throws<ArgumentException>(() => mapper.GetBrokerageSymbol(symbol));
symbol = null;
Assert.Throws<ArgumentException>(() => mapper.GetBrokerageSymbol(symbol));
symbol = Symbol.Create("", SecurityType.Forex, Market.FXCM);
Assert.Throws<ArgumentException>(() => mapper.GetBrokerageSymbol(symbol));
}
[Test]
public void ThrowsOnUnknownSymbol()
{
var mapper = new FxcmSymbolMapper();
Assert.Throws<ArgumentException>(() => mapper.GetLeanSymbol("ABC/USD", SecurityType.Forex, Market.FXCM));
var symbol = Symbol.Create("ABCUSD", SecurityType.Forex, Market.FXCM);
Assert.Throws<ArgumentException>(() => mapper.GetBrokerageSymbol(symbol));
}
[Test]
public void ThrowsOnInvalidSecurityType()
{
var mapper = new FxcmSymbolMapper();
Assert.Throws<ArgumentException>(() => mapper.GetLeanSymbol("AAPL", SecurityType.Equity, Market.FXCM));
var symbol = Symbol.Create("AAPL", SecurityType.Equity, Market.FXCM);
Assert.Throws<ArgumentException>(() => mapper.GetBrokerageSymbol(symbol));
}
[Test]
public void ChecksForKnownSymbols()
{
#pragma warning disable 0618 // This test requires implicit operators
var mapper = new FxcmSymbolMapper();
Assert.IsFalse(mapper.IsKnownBrokerageSymbol(null));
Assert.IsFalse(mapper.IsKnownBrokerageSymbol(""));
Assert.IsTrue(mapper.IsKnownBrokerageSymbol("EUR/USD"));
Assert.IsTrue(mapper.IsKnownBrokerageSymbol("GER30"));
Assert.IsFalse(mapper.IsKnownBrokerageSymbol("ABC/USD"));
Assert.IsFalse(mapper.IsKnownLeanSymbol(null));
Assert.IsFalse(mapper.IsKnownLeanSymbol(""));
Assert.IsFalse(mapper.IsKnownLeanSymbol(Symbol.Empty));
Assert.IsTrue(mapper.IsKnownLeanSymbol(Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM)));
Assert.IsFalse(mapper.IsKnownLeanSymbol(Symbol.Create("ABCUSD", SecurityType.Forex, Market.FXCM)));
Assert.IsFalse(mapper.IsKnownLeanSymbol(Symbol.Create("EURUSD", SecurityType.Cfd, Market.FXCM)));
Assert.IsFalse(mapper.IsKnownLeanSymbol(Symbol.Create("DE30EUR", SecurityType.Forex, Market.FXCM)));
#pragma warning restore 0618
}
}
}

View File

@@ -26,7 +26,6 @@ using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities;
using QuantConnect.Tests.Engine.DataFeeds;
using QuantConnect.Util;
namespace QuantConnect.Tests.Common.Securities
{

View File

@@ -77,8 +77,8 @@ namespace QuantConnect.Tests.Common.Securities
// this test asserts the portfolio behaves according to the Test_Cash algo, see TestData\CashTestingStrategy.csv
// also "https://www.dropbox.com/s/oiliumoyqqj1ovl/2013-cash.csv?dl=1"
const string fillsFile = "TestData\\test_cash_fills.xml";
const string equityFile = "TestData\\test_cash_equity.xml";
const string fillsFile = "TestData/test_cash_fills.xml";
const string equityFile = "TestData/test_cash_equity.xml";
var fills = XDocument.Load(fillsFile).Descendants("OrderEvent").Select(x => new OrderEvent(
x.Get<int>("OrderId"),
@@ -140,10 +140,10 @@ namespace QuantConnect.Tests.Common.Securities
// this test asserts the portfolio behaves according to the Test_Cash algo, but for a Forex security,
// see TestData\CashTestingStrategy.csv; also "https://www.dropbox.com/s/oiliumoyqqj1ovl/2013-cash.csv?dl=1"
const string fillsFile = "TestData\\test_forex_fills.xml";
const string equityFile = "TestData\\test_forex_equity.xml";
const string mchQuantityFile = "TestData\\test_forex_fills_mch_quantity.xml";
const string jwbQuantityFile = "TestData\\test_forex_fills_jwb_quantity.xml";
const string fillsFile = "TestData/test_forex_fills.xml";
const string equityFile = "TestData/test_forex_equity.xml";
const string mchQuantityFile = "TestData/test_forex_fills_mch_quantity.xml";
const string jwbQuantityFile = "TestData/test_forex_fills_jwb_quantity.xml";
var fills = XDocument.Load(fillsFile).Descendants("OrderEvent").Select(x => new OrderEvent(
x.Get<int>("OrderId"),

View File

@@ -15,6 +15,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Market;

View File

@@ -716,9 +716,9 @@ namespace QuantConnect.Tests.Common.Util
[Test]
[TestCase(0.072842, 3, "0.0728")]
[TestCase(0.0019999, 2, "0.002")]
[TestCase(0.0019999, 2, "0.0020")]
[TestCase(0.01234568423, 6, "0.0123457")]
public void RoundToSignificantDigits(double input, int digits, string expectedOutput)
public void RoundToSignificantDigits(decimal input, int digits, string expectedOutput)
{
var output = input.RoundToSignificantDigits(digits).ToStringInvariant();
Assert.AreEqual(expectedOutput, output);

View File

@@ -59,7 +59,7 @@ namespace QuantConnect.Tests.Compression
var fileBytes = File.ReadAllBytes(file);
var zippedBytes = QuantConnect.Compression.ZipBytes(fileBytes, "entry");
Assert.AreEqual(906121, zippedBytes.Length);
Assert.AreEqual(OS.IsWindows ? 905921 : 906121, zippedBytes.Length);
}
[Test]

View File

@@ -4,7 +4,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<RootNamespace>QuantConnect.Tests</RootNamespace>
<AssemblyName>QuantConnect.Tests</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<LangVersion>6</LangVersion>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
@@ -21,7 +21,7 @@
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<PlatformTarget>x64</PlatformTarget>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -55,22 +55,23 @@
<PackageReference Include="Common.Logging.Core" Version="3.4.1" />
<PackageReference Include="Deedle" Version="2.1.0" />
<PackageReference Include="DotNetZip" Version="1.13.3" />
<PackageReference Include="IKVM" Version="8.1.5717.0" />
<PackageReference Include="LaunchDarkly.EventSource" Version="3.3.2" />
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.6.0" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="5.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="Microsoft.TestPlatform.ObjectModel" Version="16.8.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Microsoft.TestPlatform.ObjectModel" Version="16.9.4" />
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
<PackageReference Include="Moq" Version="4.7.63" />
<PackageReference Include="NetMQ" Version="4.0.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NodaTime" Version="3.0.5" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
<PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="protobuf-net" Version="3.0.29" />
<PackageReference Include="protobuf-net.Core" Version="3.0.29" />
<PackageReference Include="RestSharp" Version="106.6.10" />
@@ -104,17 +105,6 @@
<HintPath>..\Brokerages\InteractiveBrokers\CSharpAPI.dll</HintPath>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="QuantConnect.Fxcm, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\Brokerages\Fxcm\QuantConnect.Fxcm.dll</HintPath>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="System.Configuration" />
<Reference Include="System.IdentityModel" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Web" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />

View File

@@ -1,168 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using NUnit.Framework;
using QuantConnect.Data.Custom;
using QuantConnect.ToolBox;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
namespace QuantConnect.Tests.ToolBox
{
/// <summary>
/// The rationality is to test the main functionalities:
/// - Data is correctly parsed.
/// - Data is correctly saved.
/// </summary>
[TestFixture, Ignore("FXCM API goes down on weekends")]
public class FxcmVolumeDownloaderTest
{
private string _dataDirectory;
private List<string> _testingTempFolders = new List<string>();
private FxcmVolumeDownloader _downloader;
private readonly Symbol _eurusd = Symbol.Create("EURUSD", SecurityType.Base, Market.FXCM);
[SetUp]
public void SetUpTemporatyFolder()
{
var randomFolder = Guid.NewGuid().ToStringInvariant("N").Substring(startIndex: 0, length: 8);
var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
_dataDirectory = Path.Combine(assemblyFolder, randomFolder);
_downloader = new FxcmVolumeDownloader(_dataDirectory);
_testingTempFolders.Add(_dataDirectory);
}
[TearDown]
public void CleanTemporaryFolder()
{
foreach (var testingTempFolder in _testingTempFolders)
{
if (Directory.Exists(testingTempFolder))
{
Directory.Delete(testingTempFolder, true);
}
}
}
[TestCase("./TestData/fxVolumeDaily.csv", "EURUSD", Resolution.Daily, "2016-12-01", "2017-01-30")]
[TestCase("./TestData/fxVolumeHourly.csv", "USDJPY", Resolution.Hour, "2014-12-24", "2015-01-05")]
[TestCase("./TestData/fxVolumeMinute.csv", "EURUSD", Resolution.Minute, "2012-11-23", "2012-11-27")]
public void DataIsCorrectlyParsed(string testingFilePath, string ticker, Resolution resolution, string startDate, string endDate)
{
//Arrange
var expectedData = File.ReadAllLines(testingFilePath)
.Skip(count: 1) // Skip headers.
.Select(x => x.Split(','))
.ToArray();
var symbol = Symbol.Create(ticker, SecurityType.Base, Market.FXCM);
var startUtc = Parse.DateTimeExact(startDate, "yyyy-MM-dd");
var endUtc = Parse.DateTimeExact(endDate, "yyyy-MM-dd");
//Act
var actualData = _downloader.Get(symbol, resolution, startUtc,
endUtc).Cast<FxcmVolume>().ToArray();
//Assert
Assert.AreEqual(expectedData.Length, actualData.Length);
for (var i = 0; i < expectedData.Length - 1; i++)
{
Assert.AreEqual(expectedData[i][0], actualData[i].Time.ToStringInvariant("yyyy/MM/dd HH:mm"));
Assert.AreEqual(expectedData[i][1], actualData[i].Value.ToString(CultureInfo.InvariantCulture));
Assert.AreEqual(expectedData[i][2], actualData[i].Transactions.ToString(CultureInfo.InvariantCulture));
}
}
[TestCase("GBPUSD", Resolution.Daily, "2015-11-27", 20)]
[TestCase("USDCAD", Resolution.Hour, "2016-09-15", 5)]
[TestCase("EURJPY", Resolution.Minute, "2015-01-26", 2)]
public void ParsedDataIsCorrectlySaved(string ticker, Resolution resolution, string startDate, int requestLength)
{
// Arrange
var symbol = Symbol.Create(ticker, SecurityType.Base, Market.FXCM);
var startUtc = Parse.DateTimeExact(startDate, "yyyy-MM-dd");
var endUtc = startUtc.AddDays(requestLength);
var data = _downloader.Get(symbol, resolution, startUtc, endUtc);
// Act
var writer = new FxcmVolumeWriter(resolution, symbol, _dataDirectory);
writer.Write(data);
// Assert
var expectedData = data.Cast<FxcmVolume>().ToArray();
var expectedFolder = Path.Combine(_dataDirectory, $"forex/fxcm/{resolution.ToLower()}");
if (resolution == Resolution.Minute)
{
expectedFolder = Path.Combine(expectedFolder, symbol.Value.ToLowerInvariant());
}
Assert.True(Directory.Exists(expectedFolder));
if (resolution == Resolution.Minute)
{
var zipFiles = Directory.GetFiles(expectedFolder, "*_volume.zip").Length;
// Minus one because the Downloader starts one day earlier.
Assert.AreEqual(requestLength + 1, zipFiles);
}
else
{
var expectedFilename = $"{symbol.Value.ToLowerInvariant()}_volume.zip";
Assert.True(File.Exists(Path.Combine(expectedFolder, expectedFilename)));
}
var actualdata = FxcmVolumeAuxiliaryMethods.ReadZipFolderData(expectedFolder);
Assert.AreEqual(expectedData.Length, actualdata.Count);
var lines = actualdata.Count;
for (var i = 0; i < lines - 1; i++)
{
Assert.AreEqual(expectedData[i].Value, Parse.Long(actualdata[i][1]));
Assert.AreEqual(expectedData[i].Transactions, Parse.Int(actualdata[i][2]));
}
}
[Ignore("Long test")]
[Test]
public void RequestWithMoreThan10KMinuteObservationIsCorrectlySaved()
{
// Arrange
var resolution = Resolution.Minute;
var startDate = new DateTime(year: 2013, month: 04, day: 01);
var endDate = startDate.AddMonths(months: 1);
// Act
_downloader.Run(_eurusd, resolution, startDate, endDate);
// Assert
var outputFolder = Path.Combine(_dataDirectory, "forex/fxcm/minute");
var files = Directory.GetFiles(outputFolder, "*_volume.zip", SearchOption.AllDirectories);
Assert.AreEqual(expected: 27, actual: files.Length);
}
[Ignore("Long test")]
[Test]
public void RequestWithMoreThan10KHourlyObservationIsCorrectlySaved()
{
// Arrange
var resolution = Resolution.Hour;
var startDate = new DateTime(year: 2014, month: 01, day: 01);
var endDate = startDate.AddYears(value: 3);
// Act
_downloader.Run(_eurusd, resolution, startDate, endDate);
// Assert
var outputFile = Path.Combine(_dataDirectory, "forex/fxcm/hour/eurusd_volume.zip");
var observationsCount = FxcmVolumeAuxiliaryMethods.ReadZipFileData(outputFile).Count;
// 3 years x 52 weeks x 5 days x 24 hours = 18720 hours at least.
Assert.True(observationsCount >= 18720, $"Actual observations: {observationsCount}");
}
}
}

View File

@@ -1,385 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using com.fxcm.external.api.transport;
using com.fxcm.external.api.transport.listeners;
using com.fxcm.fix;
using com.fxcm.fix.pretrade;
using com.fxcm.messaging;
using java.util;
using QuantConnect.Brokerages.Fxcm;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using TimeZone = java.util.TimeZone;
namespace QuantConnect.ToolBox.FxcmDownloader
{
/// <summary>
/// FXCM Data Downloader class
/// </summary>
public class FxcmDataDownloader : IDataDownloader, IGenericMessageListener, IStatusMessageListener
{
private readonly FxcmSymbolMapper _symbolMapper = new FxcmSymbolMapper();
private readonly string _server;
private readonly string _terminal;
private readonly string _userName;
private readonly string _password;
private IGateway _gateway;
private readonly object _locker = new object();
private string _currentRequest;
private const int ResponseTimeout = 2500;
private readonly Dictionary<string, AutoResetEvent> _mapRequestsToAutoResetEvents = new Dictionary<string, AutoResetEvent>();
private readonly Dictionary<string, TradingSecurity> _fxcmInstruments = new Dictionary<string, TradingSecurity>();
private readonly IList<BaseData> _currentBaseData = new List<BaseData>();
/// <summary>
/// Initializes a new instance of the <see cref="FxcmDataDownloader"/> class
/// </summary>
public FxcmDataDownloader(string server, string terminal, string userName, string password)
{
_server = server;
_terminal = terminal;
_userName = userName;
_password = password;
}
/// <summary>
/// Converts a Java Date value to a UTC DateTime value
/// </summary>
/// <param name="javaDate">The Java date</param>
/// <returns>A UTC DateTime value</returns>
private static DateTime FromJavaDateUtc(Date javaDate)
{
var cal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
cal.setTime(javaDate);
// note that the Month component of java.util.Date
// from 0-11 (i.e. Jan == 0)
return new DateTime(cal.get(Calendar.YEAR),
cal.get(Calendar.MONTH) + 1,
cal.get(Calendar.DAY_OF_MONTH),
cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE),
cal.get(Calendar.SECOND),
cal.get(Calendar.MILLISECOND));
}
/// <summary>
/// Checks if downloader can get the data for the Lean symbol
/// </summary>
/// <param name="symbol">The Lean symbol</param>
/// <returns>Returns true if the symbol is available</returns>
public bool HasSymbol(string symbol)
{
return _symbolMapper.IsKnownLeanSymbol(Symbol.Create(symbol, GetSecurityType(symbol), Market.FXCM));
}
/// <summary>
/// Gets the security type for the specified Lean symbol
/// </summary>
/// <param name="symbol">The Lean symbol</param>
/// <returns>The security type</returns>
public SecurityType GetSecurityType(string symbol)
{
return _symbolMapper.GetLeanSecurityType(symbol);
}
/// <summary>
/// Get historical data enumerable for a single symbol, type and resolution given this start and end time (in UTC).
/// </summary>
/// <param name="symbol">Symbol for the data we're looking for.</param>
/// <param name="resolution">Resolution of the data request</param>
/// <param name="startUtc">Start time of the data in UTC</param>
/// <param name="endUtc">End time of the data in UTC</param>
/// <returns>Enumerable of base data for this symbol</returns>
public IEnumerable<BaseData> Get(Symbol symbol, Resolution resolution, DateTime startUtc, DateTime endUtc)
{
if (!_symbolMapper.IsKnownLeanSymbol(symbol))
throw new ArgumentException("Invalid symbol requested: " + symbol.Value);
if (symbol.ID.SecurityType != SecurityType.Forex && symbol.ID.SecurityType != SecurityType.Cfd)
throw new NotSupportedException("SecurityType not available: " + symbol.ID.SecurityType);
if (endUtc <= startUtc)
throw new ArgumentException("The end date must be greater than the start date.");
Console.WriteLine("Logging in...");
// create the gateway
_gateway = GatewayFactory.createGateway();
// register the message listeners with the gateway
_gateway.registerGenericMessageListener(this);
_gateway.registerStatusMessageListener(this);
// create local login properties
var loginProperties = new FXCMLoginProperties(_userName, _password, _terminal, _server);
// log in
_gateway.login(loginProperties);
// initialize session
RequestTradingSessionStatus();
Console.WriteLine($"Downloading {resolution.ToStringInvariant()} data from {startUtc.ToStringInvariant("yyyyMMdd HH:mm:ss")} to {endUtc.ToStringInvariant("yyyyMMdd HH:mm:ss")}...");
// Find best FXCM parameters
var interval = FxcmBrokerage.ToFxcmInterval(resolution);
var totalTicks = (endUtc - startUtc).Ticks;
// download data
var totalBaseData = new List<BaseData>();
var end = endUtc;
do //
{
//show progress
progressBar(Math.Abs((end - endUtc).Ticks), totalTicks, Console.WindowWidth / 2,'█');
_currentBaseData.Clear();
var mdr = new MarketDataRequest();
mdr.setSubscriptionRequestType(SubscriptionRequestTypeFactory.SNAPSHOT);
mdr.setResponseFormat(IFixMsgTypeDefs.__Fields.MSGTYPE_FXCMRESPONSE);
mdr.setFXCMTimingInterval(interval);
mdr.setMDEntryTypeSet(MarketDataRequest.MDENTRYTYPESET_ALL);
mdr.setFXCMStartDate(new UTCDate(FxcmBrokerage.ToJavaDateUtc(startUtc)));
mdr.setFXCMStartTime(new UTCTimeOnly(FxcmBrokerage.ToJavaDateUtc(startUtc)));
mdr.setFXCMEndDate(new UTCDate(FxcmBrokerage.ToJavaDateUtc(end)));
mdr.setFXCMEndTime(new UTCTimeOnly(FxcmBrokerage.ToJavaDateUtc(end)));
mdr.addRelatedSymbol(_fxcmInstruments[_symbolMapper.GetBrokerageSymbol(symbol)]);
AutoResetEvent autoResetEvent;
lock (_locker)
{
_currentRequest = _gateway.sendMessage(mdr);
autoResetEvent = new AutoResetEvent(false);
_mapRequestsToAutoResetEvents[_currentRequest] = autoResetEvent;
}
if (!autoResetEvent.WaitOne(1000 * 5))
{
// no response, exit
break;
}
// Add data
totalBaseData.InsertRange(0, _currentBaseData.Where(x => x.Time.Date >= startUtc.Date));
if (end != _currentBaseData[0].Time)
{
// new end date = first datapoint date.
end = _currentBaseData[0].Time;
}
else
{
break;
}
} while (end > startUtc);
Console.WriteLine("\nLogging out...");
// log out
_gateway.logout();
// remove the message listeners
_gateway.removeGenericMessageListener(this);
_gateway.removeStatusMessageListener(this);
return totalBaseData.ToList();
}
private void RequestTradingSessionStatus()
{
// Note: requestTradingSessionStatus() MUST be called just after login
AutoResetEvent autoResetEvent;
lock (_locker)
{
_currentRequest = _gateway.requestTradingSessionStatus();
autoResetEvent = new AutoResetEvent(false);
_mapRequestsToAutoResetEvents[_currentRequest] = autoResetEvent;
}
if (!autoResetEvent.WaitOne(ResponseTimeout))
throw new TimeoutException("FxcmBrokerage.LoadInstruments(): Operation took " +
$"longer than {((decimal) ResponseTimeout / 1000).ToStringInvariant()} seconds."
);
}
#region IGenericMessageListener implementation
/// <summary>
/// Receives generic messages from the FXCM API
/// </summary>
/// <param name="message">Generic message received</param>
public void messageArrived(ITransportable message)
{
// Dispatch message to specific handler
lock (_locker)
{
if (message is TradingSessionStatus)
OnTradingSessionStatus((TradingSessionStatus)message);
else if (message is MarketDataSnapshot)
OnMarketDataSnapshot((MarketDataSnapshot)message);
}
}
/// <summary>
/// TradingSessionStatus message handler
/// </summary>
private void OnTradingSessionStatus(TradingSessionStatus message)
{
if (message.getRequestID() == _currentRequest)
{
// load instrument list into a dictionary
var securities = message.getSecurities();
while (securities.hasMoreElements())
{
var security = (TradingSecurity)securities.nextElement();
_fxcmInstruments[security.getSymbol()] = security;
}
_mapRequestsToAutoResetEvents[_currentRequest].Set();
_mapRequestsToAutoResetEvents.Remove(_currentRequest);
}
}
/// <summary>
/// MarketDataSnapshot message handler
/// </summary>
private void OnMarketDataSnapshot(MarketDataSnapshot message)
{
if (message.getRequestID() == _currentRequest)
{
var securityType = _symbolMapper.GetBrokerageSecurityType(message.getInstrument().getSymbol());
var symbol = _symbolMapper.GetLeanSymbol(message.getInstrument().getSymbol(), securityType, Market.FXCM);
var time = FromJavaDateUtc(message.getDate().toDate());
if (message.getFXCMTimingInterval() == FXCMTimingIntervalFactory.TICK)
{
var bid = Convert.ToDecimal(message.getBidClose());
var ask = Convert.ToDecimal(message.getAskClose());
var tick = new Tick(time, symbol, bid, ask);
//Add tick
_currentBaseData.Add(tick);
}
else // it bars
{
var open = Convert.ToDecimal((message.getBidOpen() + message.getAskOpen()) / 2);
var high = Convert.ToDecimal((message.getBidHigh() + message.getAskHigh()) / 2);
var low = Convert.ToDecimal((message.getBidLow() + message.getAskLow()) / 2);
var close = Convert.ToDecimal((message.getBidClose() + message.getAskClose()) / 2);
var bar = new TradeBar(time, symbol, open, high, low, close, 0);
// add bar to list
_currentBaseData.Add(bar);
}
if (message.getFXCMContinuousFlag() == IFixValueDefs.__Fields.FXCMCONTINUOUS_END)
{
_mapRequestsToAutoResetEvents[_currentRequest].Set();
_mapRequestsToAutoResetEvents.Remove(_currentRequest);
}
}
}
#endregion
#region IStatusMessageListener implementation
/// <summary>
/// Receives status messages from the FXCM API
/// </summary>
/// <param name="message">Status message received</param>
public void messageArrived(ISessionStatus message)
{
}
#endregion
/// <summary>
/// Aggregates a list of ticks at the requested resolution
/// </summary>
/// <param name="symbol"></param>
/// <param name="ticks"></param>
/// <param name="resolution"></param>
/// <returns></returns>
internal static IEnumerable<TradeBar> AggregateTicks(Symbol symbol, IEnumerable<Tick> ticks, TimeSpan resolution)
{
return
(from t in ticks
group t by t.Time.RoundDown(resolution)
into g
select new TradeBar
{
Symbol = symbol,
Time = g.Key,
Open = g.First().LastPrice,
High = g.Max(t => t.LastPrice),
Low = g.Min(t => t.LastPrice),
Close = g.Last().LastPrice
});
}
#region Console Helper
/// <summary>
/// Draw a progress bar
/// </summary>
/// <param name="complete"></param>
/// <param name="maxVal"></param>
/// <param name="barSize"></param>
/// <param name="progressCharacter"></param>
private static void progressBar(long complete, long maxVal, long barSize, char progressCharacter)
{
decimal p = (decimal)complete / (decimal)maxVal;
int chars = (int)Math.Floor(p / ((decimal)1 / (decimal)barSize));
string bar = string.Empty;
bar = bar.PadLeft(chars, progressCharacter);
bar = bar.PadRight(Convert.ToInt32(barSize)-1);
Console.Write($"\r[{bar}] {(p * 100).ToStringInvariant("N2")}%");
}
#endregion
}
}

View File

@@ -1,109 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using org.apache.log4j;
using QuantConnect.Configuration;
using QuantConnect.Data.Market;
using QuantConnect.Logging;
using QuantConnect.Util;
namespace QuantConnect.ToolBox.FxcmDownloader
{
public static class FxcmDownloaderProgram
{
/// <summary>
/// Primary entry point to the program
/// </summary>
public static void FxcmDownloader(IList<string> tickers, string resolution, DateTime startDate, DateTime endDate)
{
if (resolution.IsNullOrEmpty() || tickers.IsNullOrEmpty())
{
Console.WriteLine("FxcmDownloader ERROR: '--tickers=' or '--resolution=' parameter is missing");
Console.WriteLine("--tickers=eg EURUSD,USDJPY");
Console.WriteLine("--resolution=Second/Minute/Hour/Daily/All");
Environment.Exit(1);
}
try
{
Logger.getRootLogger().setLevel(Level.ERROR);
BasicConfigurator.configure(new FileAppender(new SimpleLayout(), "FxcmDownloader.log", append: false));
var allResolutions = resolution.ToLowerInvariant() == "all";
var castResolution = allResolutions ? Resolution.Tick : (Resolution)Enum.Parse(typeof(Resolution), resolution);
endDate = endDate.AddDays(1).AddMilliseconds(-1);
// Load settings from config.json
var dataDirectory = Config.Get("data-directory", "../../../Data");
var server = Config.Get("fxcm-server", "http://www.fxcorporate.com/Hosts.jsp");
var terminal = Config.Get("fxcm-terminal", "Demo");
var userName = Config.Get("fxcm-user-name", "username");
var password = Config.Get("fxcm-password", "password");
// Download the data
const string market = Market.FXCM;
var downloader = new FxcmDataDownloader(server, terminal, userName, password);
foreach (var ticker in tickers)
{
if (!downloader.HasSymbol(ticker))
throw new ArgumentException("The ticker " + ticker + " is not available.");
}
foreach (var ticker in tickers)
{
var securityType = downloader.GetSecurityType(ticker);
var symbol = Symbol.Create(ticker, securityType, market);
var data = downloader.Get(symbol, castResolution, startDate, endDate);
if (allResolutions)
{
var ticks = data.Cast<Tick>().ToList();
// Save the data (second resolution)
var writer = new LeanDataWriter(castResolution, symbol, dataDirectory);
writer.Write(ticks);
// Save the data (other resolutions)
foreach (var res in new[] { Resolution.Second, Resolution.Minute, Resolution.Hour, Resolution.Daily })
{
var resData = FxcmDataDownloader.AggregateTicks(symbol, ticks, res.ToTimeSpan());
writer = new LeanDataWriter(res, symbol, dataDirectory);
writer.Write(resData);
}
}
else
{
// Save the data (single resolution)
var writer = new LeanDataWriter(castResolution, symbol, dataDirectory);
writer.Write(data);
}
}
}
catch (Exception err)
{
Log.Error(err);
}
}
}
}

View File

@@ -1,90 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using Ionic.Zip;
using QuantConnect.Data.Custom;
namespace QuantConnect.ToolBox
{
public static class FxcmVolumeAuxiliaryMethods
{
public static IEnumerable<FxcmVolume> GetFxcmVolumeFromZip(string dataZipFile)
{
var output = new List<FxcmVolume>();
var data = ReadZipFileData(dataZipFile);
foreach (var obs in data)
{
var time = DateTime.ParseExact(obs[0], "yyyyMMdd HH:mm", CultureInfo.InvariantCulture);
output.Add(new FxcmVolume
{
DataType = MarketDataType.Base,
Time = time,
Value = Parse.Long(obs[1]),
Transactions = Parse.Int(obs[2])
});
}
return output;
}
public static List<string[]> ReadZipFolderData(string outputFolder)
{
var actualdata = new List<string[]>();
var files = Directory.GetFiles(outputFolder, "*.zip");
foreach (var file in files)
{
actualdata.AddRange(ReadZipFileData(file));
}
return actualdata;
}
public static List<string[]> ReadZipFileData(string dataZipFile)
{
var actualdata = new List<string[]>();
using (ZipFile zip = ZipFile.Read(dataZipFile))
using (var entryReader = new StreamReader(zip.First().OpenReader()))
{
while (!entryReader.EndOfStream)
{
actualdata.Add(entryReader.ReadLine().Split(','));
}
}
return actualdata;
}
public static DateTime? GetLastAvailableDateOfData(Symbol symbol, Resolution resolution, string folderPath)
{
if (!Directory.Exists(folderPath)) return null;
DateTime? lastAvailableDate = null;
switch (resolution)
{
case Resolution.Daily:
case Resolution.Hour:
var expectedFilePath = Path.Combine(folderPath, $"{symbol.Value.ToLowerInvariant()}_volume.zip"
);
if (File.Exists(expectedFilePath))
{
var lastStrDate = ReadZipFileData(expectedFilePath).Last() // last observation
.First() // first string (date)
.Substring(startIndex: 0, length: 8);
lastAvailableDate = DateTime.ParseExact(lastStrDate, DateFormat.EightCharacter, CultureInfo.InvariantCulture);
}
break;
case Resolution.Minute:
var lastFileDate = Directory
.GetFiles(folderPath, "*_volume.zip")
.OrderBy(f => f)
.LastOrDefault();
if (lastFileDate != null)
{
lastFileDate = Path.GetFileNameWithoutExtension(lastFileDate)
.Substring(startIndex: 0, length: 8);
lastAvailableDate = DateTime.ParseExact(lastFileDate, DateFormat.EightCharacter, CultureInfo.InvariantCulture);
}
break;
}
return lastAvailableDate;
}
}
}

View File

@@ -1,95 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using QuantConnect.Configuration;
using QuantConnect.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Util;
namespace QuantConnect.ToolBox.FxcmVolumeDownload
{
public static class FxcmVolumeDownloadProgram
{
public static void FxcmVolumeDownload(IList<string> tickers, string resolution, DateTime startDate, DateTime endDate)
{
var isUpdate = false;
if (resolution.IsNullOrEmpty())
{
if (!tickers.IsNullOrEmpty())
{
var _tickers = tickers.First().ToLowerInvariant();
if (_tickers == "all" || _tickers == "update")
{
if (_tickers == "update")
{
isUpdate = true;
}
tickers = new List<string> { "EURUSD","USDJPY","GBPUSD","USDCHF","EURCHF","AUDUSD","USDCAD",
"NZDUSD","EURGBP","EURJPY","GBPJPY","EURAUD","EURCAD","AUDJPY" };
resolution = "all";
}
}
else
{
Console.WriteLine("Usage:\n\t" +
"FxcmVolumeDownloader all\t will download data for all available pair for the three resolutions.\n\t" +
"FxcmVolumeDownloader update\t will download just last day data for all pair and resolutions already downloaded.");
Console.WriteLine("Usage: FxcmVolumeDownloader --tickers= --resolution= --from-date= --to-date=");
Console.WriteLine("--tickers=eg EURUSD,USDJPY\n" +
"\tAvailable pairs:\n" +
"\tEURUSD, USDJPY, GBPUSD, USDCHF, EURCHF, AUDUSD, USDCAD,\n" +
"\tNZDUSD, EURGBP, EURJPY, GBPJPY, EURAUD, EURCAD, AUDJPY");
Console.WriteLine("--resolution=Minute/Hour/Daily/All");
Environment.Exit(exitCode: 1);
}
}
try
{
Log.DebuggingEnabled = true;
Log.LogHandler = new CompositeLogHandler(new ConsoleLogHandler(), new FileLogHandler("FxcmFxVolumeDownloader.log", useTimestampPrefix: false));
var resolutions = new[] { Resolution.Daily };
if (resolution.ToLowerInvariant() == "all")
{
resolutions = new[] { Resolution.Daily, Resolution.Hour, Resolution.Minute };
}
else
{
resolutions[0] = (Resolution)Enum.Parse(typeof(Resolution), resolution);
}
// Load settings from config.json
var dataDirectory = Config.Get("data-directory", "../../../Data");
var downloader = new FxcmVolumeDownloader(dataDirectory);
foreach (var ticker in tickers)
{
var symbol = Symbol.Create(ticker, SecurityType.Base, Market.FXCM);
foreach (var _resolution in resolutions)
{
downloader.Run(symbol, _resolution, startDate, endDate, isUpdate);
}
}
}
catch (Exception err)
{
Log.Error(err);
}
}
}
}

View File

@@ -1,291 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using QuantConnect.Data;
using QuantConnect.Data.Custom;
namespace QuantConnect.ToolBox
{
/// <summary>
/// FXCM Real FOREX Volume/Transactions Downloader Toolbox Project For LEAN Algorithmic Trading Engine.
/// </summary>
/// <seealso cref="QuantConnect.ToolBox.IDataDownloader" />
public class FxcmVolumeDownloader : IDataDownloader
{
private enum FxcmSymbolId
{
EURUSD = 1,
USDJPY = 2,
GBPUSD = 3,
USDCHF = 4,
EURCHF = 5,
AUDUSD = 6,
USDCAD = 7,
NZDUSD = 8,
EURGBP = 9,
EURJPY = 10,
GBPJPY = 11,
EURAUD = 14,
EURCAD = 15,
AUDJPY = 17
}
#region Fields
/// <summary>
/// The request base URL.
/// </summary>
private readonly string _baseUrl = " http://marketsummary2.fxcorporate.com/ssisa/servlet?RT=SSI";
private readonly string _dataDirectory;
/// <summary>
/// FXCM session id.
/// </summary>
private readonly string _sid = "quantconnect";
/// <summary>
/// The columns index which should be added to obtain the transactions.
/// </summary>
private readonly long[] _transactionsIdx = {27, 29, 31, 33};
/// <summary>
/// Integer representing client version.
/// </summary>
private readonly int _ver = 1;
/// <summary>
/// The columns index which should be added to obtain the volume.
/// </summary>
private readonly int[] _volumeIdx = {26, 28, 30, 32};
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="FxcmVolumeDownloader" /> class.
/// </summary>
/// <param name="dataDirectory">The Lean data directory.</param>
public FxcmVolumeDownloader(string dataDirectory)
{
_dataDirectory = dataDirectory;
}
#region Public Methods
/// <summary>
/// Get historical data enumerable for a single symbol, type and resolution given this start and end time (in UTC).
/// </summary>
/// <param name="symbol">Symbol for the data we're looking for.</param>
/// <param name="resolution">Resolution of the data request</param>
/// <param name="startUtc">Start time of the data in UTC</param>
/// <param name="endUtc">End time of the data in UTC</param>
/// <returns>
/// Enumerable of base data for this symbol
/// </returns>
public IEnumerable<BaseData> Get(Symbol symbol, Resolution resolution, DateTime startUtc, DateTime endUtc)
{
var idx = 0;
var obsTime = startUtc;
var requestedData = new List<BaseData>();
var lines = RequestData(symbol, resolution, startUtc, endUtc);
do
{
var line = lines[idx++];
var obs = line.Split(';');
var stringDate = obs[0].Substring(startIndex: 3);
obsTime = DateTime.ParseExact(stringDate, "yyyyMMddHHmm",
DateTimeFormatInfo.InvariantInfo);
var volume = _volumeIdx.Select(x => Parse.Long(obs[x])).Sum();
var transactions = _transactionsIdx.Select(x => Parse.Int(obs[x])).Sum();
requestedData.Add(new FxcmVolume
{
Symbol = symbol,
Time = obsTime,
Value = volume,
Transactions = transactions
});
} while (obsTime.Date <= endUtc.Date && idx < lines.Length - 1);
return requestedData.Where(o => o.Time.Date >= startUtc.Date && o.Time.Date <= endUtc.Date);
}
/// <summary>
/// Method in charge of making all the steps to save the data. It makes the request, parses the data and saves it.
/// This method takes into account the size limitation of the responses, slicing big request into smaller ones.
/// </summary>
/// <param name="symbol">The symbol.</param>
/// <param name="resolution">The resolution.</param>
/// <param name="startUtc">The start UTC.</param>
/// <param name="endUtc">The end UTC.</param>
/// <param name="update">Flag to </param>
public void Run(Symbol symbol, Resolution resolution, DateTime startUtc, DateTime endUtc, bool update = false)
{
var data = new List<BaseData>();
var requestDayInterval = 0;
var writer = new FxcmVolumeWriter(resolution, symbol, _dataDirectory);
var intermediateStartDate = startUtc;
var intermediateEndDate = endUtc;
if (update)
{
var updatedStartDate = FxcmVolumeAuxiliaryMethods.GetLastAvailableDateOfData(symbol, resolution, writer.FolderPath);
if (updatedStartDate == null) return;
intermediateStartDate = ((DateTime) updatedStartDate).AddDays(value: -1);
intermediateEndDate = DateTime.Today;
}
// As the responses has a Limit of 10000 lines, hourly data the minute data request should be sliced.
if (resolution == Resolution.Minute && (endUtc - startUtc).TotalMinutes > 10000)
{
// Six days are 8640 minute observations, 7 days are 10080.
requestDayInterval = 6;
}
else if (resolution == Resolution.Hour && (endUtc - startUtc).TotalHours > 10000)
{
// 410 days x 24 hr = 9840 hr.
requestDayInterval = 410;
}
var counter = 0;
do
{
if (requestDayInterval != 0)
{
if (counter++ != 0)
{
intermediateStartDate = intermediateEndDate.AddDays(value: 1);
}
intermediateEndDate = intermediateStartDate.AddDays(requestDayInterval);
if (intermediateEndDate > endUtc) intermediateEndDate = endUtc;
}
data.AddRange(Get(symbol, resolution, intermediateStartDate, intermediateEndDate));
// For every 300k observations in memory, write it.
if (resolution == Resolution.Minute && counter % 30 == 0)
{
writer.Write(data);
data.Clear();
}
} while (intermediateEndDate != endUtc);
writer.Write(data, update);
}
#endregion
#region Private Methods
/// <summary>
/// Gets the FXCM identifier from a FOREX pair symbol.
/// </summary>
/// <param name="symbol">The pair symbol.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentException">Volume data is not available for the selected symbol. - symbol</exception>
private int GetFxcmIDFromSymbol(Symbol symbol)
{
int symbolId;
try
{
symbolId = (int) Enum.Parse(typeof(FxcmSymbolId), symbol.Value);
}
catch (ArgumentException)
{
throw new ArgumentException("Volume data is not available for the selected symbol.", "symbol");
}
return symbolId;
}
/// <summary>
/// Gets the string interval representation from the resolution.
/// </summary>
/// <param name="resolution">The requested resolution.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentOutOfRangeException">
/// resolution - tick or second resolution are not supported for Forex
/// Volume.
/// </exception>
private string GetIntervalFromResolution(Resolution resolution)
{
string interval;
switch (resolution)
{
case Resolution.Minute:
interval = "M1";
break;
case Resolution.Hour:
interval = "H1";
break;
case Resolution.Daily:
interval = "D1";
break;
default:
throw new ArgumentOutOfRangeException(nameof(resolution), resolution,
"tick or second resolution are not supported for Forex Volume.");
}
return interval;
}
/// <summary>
/// Generates the API Requests.
/// </summary>
/// <param name="symbol">The symbol.</param>
/// <param name="resolution">The resolution.</param>
/// <param name="startUtc">The start date in UTC.</param>
/// <param name="endUtc">The end date in UTC.</param>
/// <returns></returns>
private string[] RequestData(Symbol symbol, Resolution resolution, DateTime startUtc, DateTime endUtc)
{
var startDate = string.Empty;
var endDate = endUtc.AddDays(value: 2).ToStringInvariant("yyyyMMdd") + "2100";
var symbolId = GetFxcmIDFromSymbol(symbol);
var interval = GetIntervalFromResolution(resolution);
switch (resolution)
{
case Resolution.Minute:
case Resolution.Hour:
startDate = startUtc.ToStringInvariant("yyyyMMdd") + "0000";
break;
case Resolution.Daily:
startDate = startUtc.AddDays(value: 1).ToStringInvariant("yyyyMMdd") + "2100";
break;
}
var request = $"{_baseUrl}&ver={_ver}&sid={_sid}&interval={interval}&offerID={symbolId}" +
$"&timeFrom={startDate.ToStringInvariant()}&timeTo={endDate.ToStringInvariant()}";
string[] lines;
using (var client = new WebClient())
{
var data = client.DownloadString(request);
lines = data.Split('\n');
}
// Removes the HTML head and tail.
return lines.Skip(count: 2).Take(lines.Length - 4).ToArray();
}
#endregion
}
}

View File

@@ -1,107 +0,0 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using QuantConnect.Data;
using QuantConnect.Data.Custom;
namespace QuantConnect.ToolBox
{
public class FxcmVolumeWriter
{
#region Fields
private readonly Resolution _resolution;
private readonly Symbol _symbol;
#endregion
public string FolderPath { get; }
public FxcmVolumeWriter(Resolution resolution, Symbol symbol, string dataDirectory)
{
_symbol = symbol;
_resolution = resolution;
var market = _symbol.ID.Market;
FolderPath =
Path.Combine(new[] {dataDirectory, "forex", market.ToLowerInvariant(), _resolution.ToLower()});
if (_resolution == Resolution.Minute)
{
FolderPath = Path.Combine(FolderPath, _symbol.Value.ToLowerInvariant());
}
}
public void Write(IEnumerable<BaseData> data, bool update = false)
{
if (!Directory.Exists(FolderPath)) Directory.CreateDirectory(FolderPath);
if (update && _resolution != Resolution.Minute)
{
data = ReadActualDataAndAppendNewData(data);
}
// Seems the data has some duplicate values! This makes the writer throws an error. So, just in case, we clean the data from duplicates.
data = data.GroupBy(x => x.Time)
.Select(g => g.First());
if (_resolution == Resolution.Minute)
{
WriteMinuteData(data);
}
else
{
WriteHourAndDailyData(data);
}
}
#region Private Methods
private IEnumerable<BaseData> ReadActualDataAndAppendNewData(IEnumerable<BaseData> data)
{
// Read the actual data
var zipFilePath = Path.Combine(FolderPath, _symbol.Value.ToLowerInvariant() + "_volume.zip");
var actualData = FxcmVolumeAuxiliaryMethods.GetFxcmVolumeFromZip(zipFilePath);
return actualData.Concat(data);
}
private void WriteHourAndDailyData(IEnumerable<BaseData> data)
{
var sb = new StringBuilder();
var volData = data.Cast<FxcmVolume>();
foreach (var obs in volData)
{
sb.AppendLine($"{obs.Time.ToStringInvariant("yyyyMMdd HH:mm")},{obs.Value.ToStringInvariant()},{obs.Transactions}");
}
var data_to_save = sb.ToString();
var filename = _symbol.Value.ToLowerInvariant() + "_volume";
var csvFilePath = Path.Combine(FolderPath, filename + ".csv");
var zipFilePath = csvFilePath.Replace(".csv", ".zip");
if (File.Exists(zipFilePath)) File.Delete(zipFilePath);
File.WriteAllText(csvFilePath, data_to_save);
// Write out this data string to a zip file
Compression.Zip(csvFilePath, Path.GetFileName(csvFilePath));
}
private void WriteMinuteData(IEnumerable<BaseData> data)
{
var sb = new StringBuilder();
var volData = data.Cast<FxcmVolume>();
var dataByDay = volData.GroupBy(o => o.Time.Date);
foreach (var dayOfData in dataByDay)
{
foreach (var obs in dayOfData)
{
sb.AppendLine($"{obs.Time.TimeOfDay.TotalMilliseconds.ToStringInvariant()},{obs.Value.ToStringInvariant()},{obs.Transactions}");
}
var filename = $"{dayOfData.Key.ToStringInvariant("yyyyMMdd")}_volume.csv";
var filePath = Path.Combine(FolderPath, filename);
File.WriteAllText(filePath, sb.ToString());
// Write out this data string to a zip file
Compression.Zip(filePath, filename);
sb.Clear();
}
}
#endregion
}
}

View File

@@ -15,10 +15,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Market;
using QuantConnect.Util;
namespace QuantConnect.ToolBox
{

View File

@@ -15,7 +15,6 @@
*/
using System.Diagnostics;
using Microsoft.Win32;
using System.Threading;
using QuantConnect.Configuration;
@@ -41,29 +40,6 @@ namespace QuantConnect.ToolBox.IQFeed
);
}
public string getPath()
{
var key = Registry.LocalMachine.OpenSubKey("SOFTWARE\\DTN\\IQFeed");
if (key == null)
{
// if it isn't in that location, it is possible the user is running and x64 OS. Check the windows virtualized registry location
key = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Wow6432Node\\DTN\\IQFeed");
}
string sLocation = null;
if (key != null)
{
sLocation = key.GetValue("EXEDIR", "").ToString();
// close the key since we don't need it anymore
key.Close();
// verify there is a \ on the end before we append the exe name
if (!(sLocation.EndsWith("\\") || sLocation.EndsWith("/")))
{
sLocation += "\\";
}
sLocation += "IQConnect.exe";
}
return sLocation;
}
public string getArguments(IQCredentials iqc)
{
var arguments = "";

View File

@@ -19,7 +19,6 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using ikvm.extensions;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Util;
@@ -133,7 +132,7 @@ namespace QuantConnect.ToolBox.IVolatilityEquityConverter
private static Symbol GetSymbol(string fileName)
{
var splits = fileName.Split('_');
var ticker = splits[1].toLowerCase();
var ticker = splits[1].ToLowerInvariant();
return Symbol.Create(ticker, SecurityType.Equity, Market.USA);
}
@@ -144,7 +143,7 @@ namespace QuantConnect.ToolBox.IVolatilityEquityConverter
/// <returns></returns>
private static Resolution ParseResolution(string entry)
{
switch (entry.Trim().toLowerCase())
switch (entry.Trim().ToLowerInvariant())
{
case "minute":
return Resolution.Minute;

View File

@@ -14,8 +14,8 @@
*/
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Util;
namespace QuantConnect.ToolBox
{

View File

@@ -28,8 +28,6 @@ using QuantConnect.ToolBox.CoinApiDataConverter;
using QuantConnect.ToolBox.CryptoiqDownloader;
using QuantConnect.ToolBox.DukascopyDownloader;
using QuantConnect.ToolBox.EstimizeDataDownloader;
using QuantConnect.ToolBox.FxcmDownloader;
using QuantConnect.ToolBox.FxcmVolumeDownload;
using QuantConnect.ToolBox.GDAXDownloader;
using QuantConnect.ToolBox.IBDownloader;
using QuantConnect.ToolBox.IEX;
@@ -96,14 +94,6 @@ namespace QuantConnect.ToolBox
case "dukascopydownloader":
DukascopyDownloaderProgram.DukascopyDownloader(tickers, resolution, fromDate, toDate);
break;
case "fdl":
case "fxcmdownloader":
FxcmDownloaderProgram.FxcmDownloader(tickers, resolution, fromDate, toDate);
break;
case "fvdl":
case "fxcmvolumedownload":
FxcmVolumeDownloadProgram.FxcmVolumeDownload(tickers, resolution, fromDate, toDate);
break;
case "ibdl":
case "ibdownloader":
IBDownloaderProgram.IBDownloader(tickers, resolution, fromDate, toDate);

View File

@@ -5,7 +5,7 @@
<OutputType>Exe</OutputType>
<RootNamespace>QuantConnect.ToolBox</RootNamespace>
<AssemblyName>QuantConnect.ToolBox</AssemblyName>
<TargetFramework>net462</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>6</LangVersion>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
@@ -57,7 +57,6 @@
<PackageReference Include="Common.Logging" Version="3.4.1" />
<PackageReference Include="Common.Logging.Core" Version="3.4.1" />
<PackageReference Include="DotNetZip" Version="1.13.3" />
<PackageReference Include="IKVM" Version="8.1.5717.0" />
<PackageReference Include="IQFeed.CSharpApiClient" Version="2.5.1" />
<PackageReference Include="LaunchDarkly.EventSource" Version="3.3.2" />
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.6.0" />
@@ -99,16 +98,6 @@
<PackageReference Include="Utf8Json" Version="1.3.7" />
<PackageReference Include="WebSocket4Net" Version="0.15.2" />
</ItemGroup>
<ItemGroup>
<Reference Include="QuantConnect.Fxcm">
<HintPath>..\Brokerages\Fxcm\QuantConnect.Fxcm.dll</HintPath>
</Reference>
<Reference Include="System.Configuration" />
<Reference Include="System.Net" />
<Reference Include="System.Web" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<None Include="..\LICENSE">
<Pack>True</Pack>

View File

@@ -66,8 +66,11 @@ namespace QuantConnect.ToolBox.RandomDataGenerator
CoarseUniverseGeneratorProgram.CoarseUniverseGenerator();
}
output.Info.WriteLine("Press any key to exit...");
Console.ReadKey();
if (!Console.IsInputRedirected)
{
output.Info.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
public static DateTime GetDateMidpoint(DateTime start, DateTime end)

View File

@@ -76,56 +76,23 @@ Visual Studio will automatically start to restore the Nuget packages. If not, in
- In the menu bar, click `Run > Start Debugging`.
Alternatively, run the compiled `exe` file. First, in the menu bar, click `Build > Build All`, then:
Alternatively, run the compiled `dll` file. First, in the menu bar, click `Build > Build All`, then:
```
cd Lean/Launcher/bin/Debug
mono QuantConnect.Lean.Launcher.exe
dotnet QuantConnect.Lean.Launcher.dll
```
### Linux (Debian, Ubuntu)
- Install [Mono](http://www.mono-project.com/download/#download-lin):
- Install [dotnet 5](https://docs.microsoft.com/en-us/dotnet/core/install/linux):
- Compile Lean Solution:
```
sudo apt-get update && sudo rm -rf /var/lib/apt/lists/*
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb http://download.mono-project.com/repo/ubuntu stable-xenial/snapshots/5.12.0.226 main" > /etc/apt/sources.list.d/mono-xamarin.list && \
apt-get update && apt-get install -y binutils mono-complete ca-certificates-mono mono-vbnc nuget referenceassemblies-pcl && \
apt-get install -y fsharp && rm -rf /var/lib/apt/lists/* /tmp/*
dotnet QuantConnect.Lean.sln
```
If you get this error on the last command:
**Unable to locate package referenceassemblies-pcl**,
run the following command (it works on current version of Ubuntu - 17.10):
```
echo "deb http://download.mono-project.com/repo/ubuntu xenial main" | sudo tee /etc/apt/sources.list.d/mono-official.list
```
```
sudo apt-get update
sudo apt-get install -y binutils mono-complete ca-certificates-mono referenceassemblies-pcl fsharp
```
- Install Nuget
```
sudo apt-get update && sudo apt-get install -y nuget
```
- Restore NuGet packages then compile:
```
nuget restore QuantConnect.Lean.sln
msbuild QuantConnect.Lean.sln
```
If you get: "Error initializing task Fsc: Not registered task Fsc." -> `sudo apt-get upgrade mono-complete`
If you get: "XX not found" -> Make sure Nuget ran successfully, and re-run if neccessary.
If you get: "Confirm ... '.../QuantConnect.XX.csproj.*.props' is correct, and that the file exists on disk." -> Ensure that your installation path is free of [reserved characters](https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file)
If you get other errors that lead to the failure of your building, please refer to the commands in "DockerfileLeanFoundation" file for help.
- Run the compiled `exe` file:
- Run Lean:
```
cd Launcher/bin/Debug
mono ./QuantConnect.Lean.Launcher.exe
dotnet QuantConnect.Lean.Launcher.dll
```
- Interactive Brokers set up details
@@ -140,15 +107,13 @@ If after all you still receive connection refuse error, try changing the `ib-por
- Build the solution by clicking Build Menu -> Build Solution (this should trigger the Nuget package restore)
- Press `F5` to run
Nuget packages not being restored is the most common build issue. By default Visual Studio includes NuGet, if your installation of Visual Studio (or your IDE) cannot find DLL references, install [Nuget](https://www.nuget.org/), run nuget on the solution and re-build the Solution again.
### Python Support
A full explanation of the Python installation process can be found in the [Algorithm.Python](https://github.com/QuantConnect/Lean/tree/master/Algorithm.Python#quantconnect-python-algorithm-project) project.
### Local-Cloud Hybrid Development.
You can develop in your IDE and synchronize to the cloud with Skylight. For more information please see the [Skylight Home](https://www.quantconnect.com/skylight).
Seamlessly develop locally in your favorite development environment, with full autocomplete and debugging support to quickly and easily identify problems with your strategy. For more information please see the [CLI Home](https://www.quantconnect.com/cli).
## Issues and Feature Requests ##