Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
854b987cd0 | ||
|
|
a26414d273 | ||
|
|
84264ca7ef | ||
|
|
b2ed398687 | ||
|
|
e8c316cbcf | ||
|
|
724e52c0b3 | ||
|
|
4252c79e45 | ||
|
|
cbb40dfa43 | ||
|
|
8792fa2600 | ||
|
|
20e9fd7899 | ||
|
|
e05a6bffd0 | ||
|
|
c2f0fdc47a | ||
|
|
b1b8da1e17 | ||
|
|
01a0454c57 | ||
|
|
90e2c48404 | ||
|
|
888c443264 | ||
|
|
03efc1b735 | ||
|
|
6ef2ead929 | ||
|
|
bfd319c91e | ||
|
|
5f61456df8 | ||
|
|
a46a551c03 | ||
|
|
cf9b547e2e | ||
|
|
faa4e91e04 | ||
|
|
cc83f19528 | ||
|
|
54af12b06a | ||
|
|
1d1c8f5f82 | ||
|
|
3966c0e91f | ||
|
|
28160e1301 | ||
|
|
f4679785a5 | ||
|
|
79b9009452 | ||
|
|
e5b5f80d9d | ||
|
|
027fde8f09 | ||
|
|
c7ccd60bf2 | ||
|
|
b30bb3fcf5 | ||
|
|
40a87eb056 | ||
|
|
035b29fdf5 | ||
|
|
f2fc1aae9e | ||
|
|
ff5fc5db5d | ||
|
|
40c3062348 | ||
|
|
371f2cd469 | ||
|
|
090ceb131e | ||
|
|
6885fd130b | ||
|
|
a450cea8d0 | ||
|
|
934128cfa0 | ||
|
|
c7a74306fb | ||
|
|
cfacc755fa |
164
Algorithm.CSharp/AddOptionContractExpiresRegressionAlgorithm.cs
Normal file
164
Algorithm.CSharp/AddOptionContractExpiresRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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.Linq;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// We add an option contract using <see cref="QCAlgorithm.AddOptionContract"/> and place a trade and wait till it expires
|
||||
/// later will liquidate the resulting equity position and assert both option and underlying get removed
|
||||
/// </summary>
|
||||
public class AddOptionContractExpiresRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private DateTime _expiration = new DateTime(2014, 06, 21);
|
||||
private Symbol _option;
|
||||
private Symbol _twx;
|
||||
private bool _traded;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2014, 06, 05);
|
||||
SetEndDate(2014, 06, 30);
|
||||
|
||||
_twx = QuantConnect.Symbol.Create("TWX", SecurityType.Equity, Market.USA);
|
||||
|
||||
AddUniverse("my-daily-universe-name", time => new List<string> { "AAPL" });
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (_option == null)
|
||||
{
|
||||
var option = OptionChainProvider.GetOptionContractList(_twx, Time)
|
||||
.OrderBy(symbol => symbol.ID.Symbol)
|
||||
.FirstOrDefault(optionContract => optionContract.ID.Date == _expiration
|
||||
&& optionContract.ID.OptionRight == OptionRight.Call
|
||||
&& optionContract.ID.OptionStyle == OptionStyle.American);
|
||||
if (option != null)
|
||||
{
|
||||
_option = AddOptionContract(option).Symbol;
|
||||
}
|
||||
}
|
||||
|
||||
if (_option != null && Securities[_option].Price != 0 && !_traded)
|
||||
{
|
||||
_traded = true;
|
||||
Buy(_option, 1);
|
||||
|
||||
foreach (var symbol in new [] { _option, _option.Underlying })
|
||||
{
|
||||
var config = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(symbol).ToList();
|
||||
|
||||
if (!config.Any())
|
||||
{
|
||||
throw new Exception($"Was expecting configurations for {symbol}");
|
||||
}
|
||||
if (config.Any(dataConfig => dataConfig.DataNormalizationMode != DataNormalizationMode.Raw))
|
||||
{
|
||||
throw new Exception($"Was expecting DataNormalizationMode.Raw configurations for {symbol}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Time.Date > _expiration)
|
||||
{
|
||||
if (SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(_option).Any())
|
||||
{
|
||||
throw new Exception($"Unexpected configurations for {_option} after it has been delisted");
|
||||
}
|
||||
|
||||
if (Securities[_twx].Invested)
|
||||
{
|
||||
if (!SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(_twx).Any())
|
||||
{
|
||||
throw new Exception($"Was expecting configurations for {_twx}");
|
||||
}
|
||||
|
||||
// first we liquidate the option exercised position
|
||||
Liquidate(_twx);
|
||||
}
|
||||
}
|
||||
else if (Time.Date > _expiration && !Securities[_twx].Invested)
|
||||
{
|
||||
if (SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(_twx).Any())
|
||||
{
|
||||
throw new Exception($"Unexpected configurations for {_twx} after it has been liquidated");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "3"},
|
||||
{"Average Win", "2.73%"},
|
||||
{"Average Loss", "-2.98%"},
|
||||
{"Compounding Annual Return", "-4.619%"},
|
||||
{"Drawdown", "0.300%"},
|
||||
{"Expectancy", "-0.042"},
|
||||
{"Net Profit", "-0.332%"},
|
||||
{"Sharpe Ratio", "-3.7"},
|
||||
{"Probabilistic Sharpe Ratio", "0.563%"},
|
||||
{"Loss Rate", "50%"},
|
||||
{"Win Rate", "50%"},
|
||||
{"Profit-Loss Ratio", "0.92"},
|
||||
{"Alpha", "-0.023"},
|
||||
{"Beta", "0.005"},
|
||||
{"Annual Standard Deviation", "0.006"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-3.424"},
|
||||
{"Tracking Error", "0.057"},
|
||||
{"Treynor Ratio", "-4.775"},
|
||||
{"Total Fees", "$2.00"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-43.418"},
|
||||
{"Return Over Maximum Drawdown", "-14.274"},
|
||||
{"Portfolio Turnover", "0.007"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1185639451"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* 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.Data;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Interfaces;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// We add an option contract using <see cref="QCAlgorithm.AddOptionContract"/> and place a trade, the underlying
|
||||
/// gets deselected from the universe selection but should still be present since we manually added the option contract.
|
||||
/// Later we call <see cref="QCAlgorithm.RemoveOptionContract"/> and expect both option and underlying to be removed.
|
||||
/// </summary>
|
||||
public class AddOptionContractFromUniverseRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private DateTime _expiration = new DateTime(2014, 06, 21);
|
||||
private SecurityChanges _securityChanges = SecurityChanges.None;
|
||||
private Symbol _option;
|
||||
private Symbol _aapl;
|
||||
private Symbol _twx;
|
||||
private bool _traded;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_twx = QuantConnect.Symbol.Create("TWX", SecurityType.Equity, Market.USA);
|
||||
_aapl = QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA);
|
||||
UniverseSettings.Resolution = Resolution.Minute;
|
||||
UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw;
|
||||
|
||||
SetStartDate(2014, 06, 05);
|
||||
SetEndDate(2014, 06, 09);
|
||||
|
||||
AddUniverse(enumerable => new[] { Time.Date <= new DateTime(2014, 6, 5) ? _twx : _aapl },
|
||||
enumerable => new[] { Time.Date <= new DateTime(2014, 6, 5) ? _twx : _aapl });
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (_option != null && Securities[_option].Price != 0 && !_traded)
|
||||
{
|
||||
_traded = true;
|
||||
Buy(_option, 1);
|
||||
}
|
||||
|
||||
if (Time.Date > new DateTime(2014, 6, 5))
|
||||
{
|
||||
if (Time < new DateTime(2014, 6, 6, 14, 0, 0))
|
||||
{
|
||||
var configs = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(_twx);
|
||||
// assert underlying still there after the universe selection removed it, still used by the manually added option contract
|
||||
if (!configs.Any())
|
||||
{
|
||||
throw new Exception($"Was expecting configurations for {_twx}" +
|
||||
$" even after it has been deselected from coarse universe because we still have the option contract.");
|
||||
}
|
||||
}
|
||||
else if (Time == new DateTime(2014, 6, 6, 14, 0, 0))
|
||||
{
|
||||
// liquidate & remove the option
|
||||
RemoveOptionContract(_option);
|
||||
}
|
||||
// assert underlying was finally removed
|
||||
else if(Time > new DateTime(2014, 6, 6, 14, 0, 0))
|
||||
{
|
||||
foreach (var symbol in new[] { _option, _option.Underlying })
|
||||
{
|
||||
var configs = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(symbol);
|
||||
if (configs.Any())
|
||||
{
|
||||
throw new Exception($"Unexpected configuration for {symbol} after it has been deselected from coarse universe and option contract is removed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSecuritiesChanged(SecurityChanges changes)
|
||||
{
|
||||
if (_securityChanges.RemovedSecurities.Intersect(changes.RemovedSecurities).Any())
|
||||
{
|
||||
throw new Exception($"SecurityChanges.RemovedSecurities intersect {changes.RemovedSecurities}. We expect no duplicate!");
|
||||
}
|
||||
if (_securityChanges.AddedSecurities.Intersect(changes.AddedSecurities).Any())
|
||||
{
|
||||
throw new Exception($"SecurityChanges.AddedSecurities intersect {changes.RemovedSecurities}. We expect no duplicate!");
|
||||
}
|
||||
// keep track of all removed and added securities
|
||||
_securityChanges += changes;
|
||||
|
||||
if (changes.AddedSecurities.Any(security => security.Symbol.SecurityType == SecurityType.Option))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var addedSecurity in changes.AddedSecurities)
|
||||
{
|
||||
var option = OptionChainProvider.GetOptionContractList(addedSecurity.Symbol, Time)
|
||||
.OrderBy(symbol => symbol.ID.Symbol)
|
||||
.First(optionContract => optionContract.ID.Date == _expiration
|
||||
&& optionContract.ID.OptionRight == OptionRight.Call
|
||||
&& optionContract.ID.OptionStyle == OptionStyle.American);
|
||||
AddOptionContract(option);
|
||||
|
||||
foreach (var symbol in new[] { option, option.Underlying })
|
||||
{
|
||||
var config = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(symbol).ToList();
|
||||
|
||||
if (!config.Any())
|
||||
{
|
||||
throw new Exception($"Was expecting configurations for {symbol}");
|
||||
}
|
||||
if (config.Any(dataConfig => dataConfig.DataNormalizationMode != DataNormalizationMode.Raw))
|
||||
{
|
||||
throw new Exception($"Was expecting DataNormalizationMode.Raw configurations for {symbol}");
|
||||
}
|
||||
}
|
||||
|
||||
// just keep the first we got
|
||||
if (_option == null)
|
||||
{
|
||||
_option = option;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (SubscriptionManager.Subscriptions.Any(dataConfig => dataConfig.Symbol == _twx || dataConfig.Symbol.Underlying == _twx))
|
||||
{
|
||||
throw new Exception($"Was NOT expecting any configurations for {_twx} or it's options, since we removed the contract");
|
||||
}
|
||||
|
||||
if (SubscriptionManager.Subscriptions.All(dataConfig => dataConfig.Symbol != _aapl))
|
||||
{
|
||||
throw new Exception($"Was expecting configurations for {_aapl}");
|
||||
}
|
||||
if (SubscriptionManager.Subscriptions.All(dataConfig => dataConfig.Symbol.Underlying != _aapl))
|
||||
{
|
||||
throw new Exception($"Was expecting options configurations for {_aapl}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "-0.23%"},
|
||||
{"Compounding Annual Return", "-15.596%"},
|
||||
{"Drawdown", "0.200%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Net Profit", "-0.232%"},
|
||||
{"Sharpe Ratio", "-7.739"},
|
||||
{"Probabilistic Sharpe Ratio", "1.216%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.027"},
|
||||
{"Beta", "-0.174"},
|
||||
{"Annual Standard Deviation", "0.006"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-11.586"},
|
||||
{"Tracking Error", "0.042"},
|
||||
{"Treynor Ratio", "0.286"},
|
||||
{"Total Fees", "$2.00"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-19.883"},
|
||||
{"Return Over Maximum Drawdown", "-67.224"},
|
||||
{"Portfolio Turnover", "0.014"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "721476625"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
{
|
||||
if (!_equityBought && data.ContainsKey(_spy)) {
|
||||
//Buy our Equity
|
||||
var quantity = CalculateOrderQuantity(_spy, .1m);
|
||||
@@ -114,7 +114,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
/// <param name="orderEvent">OrderEvent object that contains all the information about the event</param>
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
{
|
||||
// Get the order from our transactions
|
||||
var order = Transactions.GetOrderById(orderEvent.OrderId);
|
||||
|
||||
@@ -147,7 +147,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
// All PartiallyFilled orders should have a LastFillTime
|
||||
case OrderStatus.PartiallyFilled:
|
||||
if (order.LastFillTime == null)
|
||||
if (order.LastFillTime == null)
|
||||
{
|
||||
throw new Exception("LastFillTime should not be null");
|
||||
}
|
||||
@@ -183,9 +183,9 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
throw new Exception("OptionExercise order price should be strike price!!");
|
||||
}
|
||||
|
||||
if (orderEvent.Quantity != 1)
|
||||
if (orderEvent.Quantity != -1)
|
||||
{
|
||||
throw new Exception("OrderEvent Quantity should be 1");
|
||||
throw new Exception("OrderEvent Quantity should be -1");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Treynor Ratio", "-0.018"},
|
||||
{"Total Fees", "$2.00"},
|
||||
{"Fitness Score", "0.213"},
|
||||
{"OrderListHash", "-1514011542"}
|
||||
{"OrderListHash", "904167951"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,4 +119,4 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"OrderListHash", "491919591"}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,7 +242,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1073240275"}
|
||||
{"OrderListHash", "415415696"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1935621950"}
|
||||
{"OrderListHash", "687310345"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1708974186"}
|
||||
{"OrderListHash", "737971736"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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.Data;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Interfaces;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Demonstration of how to chain a coarse and fine universe selection with an option chain universe selection model
|
||||
/// that will add and remove an <see cref="OptionChainUniverse"/> for each symbol selected on fine
|
||||
/// </summary>
|
||||
public class CoarseFineOptionUniverseChainRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
// initialize our changes to nothing
|
||||
private SecurityChanges _changes = SecurityChanges.None;
|
||||
private int _optionCount;
|
||||
private Symbol _lastEquityAdded;
|
||||
private Symbol _aapl;
|
||||
private Symbol _twx;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_twx = QuantConnect.Symbol.Create("TWX", SecurityType.Equity, Market.USA);
|
||||
_aapl = QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA);
|
||||
UniverseSettings.Resolution = Resolution.Minute;
|
||||
|
||||
SetStartDate(2014, 06, 05);
|
||||
SetEndDate(2014, 06, 06);
|
||||
|
||||
var selectionUniverse = AddUniverse(enumerable => new[] { Time.Date <= new DateTime(2014, 6, 5) ? _twx : _aapl },
|
||||
enumerable => new[] { Time.Date <= new DateTime(2014, 6, 5) ? _twx : _aapl });
|
||||
|
||||
AddUniverseOptions(selectionUniverse, universe =>
|
||||
{
|
||||
if (universe.Underlying == null)
|
||||
{
|
||||
throw new Exception("Underlying data point is null! This shouldn't happen, each OptionChainUniverse handles and should provide this");
|
||||
}
|
||||
return universe.IncludeWeeklys()
|
||||
.FrontMonth()
|
||||
.Contracts(universe.Take(5));
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
// if we have no changes, do nothing
|
||||
if (_changes == SecurityChanges.None ||
|
||||
_changes.AddedSecurities.Any(security => security.Price == 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// liquidate removed securities
|
||||
foreach (var security in _changes.RemovedSecurities)
|
||||
{
|
||||
if (security.Invested)
|
||||
{
|
||||
Liquidate(security.Symbol);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var security in _changes.AddedSecurities)
|
||||
{
|
||||
if (!security.Symbol.HasUnderlying)
|
||||
{
|
||||
_lastEquityAdded = security.Symbol;
|
||||
}
|
||||
else
|
||||
{
|
||||
// options added should all match prev added security
|
||||
if (security.Symbol.Underlying != _lastEquityAdded)
|
||||
{
|
||||
throw new Exception($"Unexpected symbol added {security.Symbol}");
|
||||
}
|
||||
|
||||
_optionCount++;
|
||||
}
|
||||
|
||||
SetHoldings(security.Symbol, 0.05m);
|
||||
|
||||
var config = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(security.Symbol).ToList();
|
||||
|
||||
if (!config.Any())
|
||||
{
|
||||
throw new Exception($"Was expecting configurations for {security.Symbol}");
|
||||
}
|
||||
if (config.Any(dataConfig => dataConfig.DataNormalizationMode != DataNormalizationMode.Raw))
|
||||
{
|
||||
throw new Exception($"Was expecting DataNormalizationMode.Raw configurations for {security.Symbol}");
|
||||
}
|
||||
}
|
||||
_changes = SecurityChanges.None;
|
||||
}
|
||||
|
||||
public override void OnSecuritiesChanged(SecurityChanges changes)
|
||||
{
|
||||
_changes += changes;
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
var config = SubscriptionManager.Subscriptions.ToList();
|
||||
if (config.Any(dataConfig => dataConfig.Symbol == _twx || dataConfig.Symbol.Underlying == _twx))
|
||||
{
|
||||
throw new Exception($"Was NOT expecting any configurations for {_twx} or it's options, since coarse/fine should have deselected it");
|
||||
}
|
||||
|
||||
if (_optionCount == 0)
|
||||
{
|
||||
throw new Exception("Option universe chain did not add any option!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "13"},
|
||||
{"Average Win", "0.65%"},
|
||||
{"Average Loss", "-0.05%"},
|
||||
{"Compounding Annual Return", "3216040423556140000000000%"},
|
||||
{"Drawdown", "0.500%"},
|
||||
{"Expectancy", "1.393"},
|
||||
{"Net Profit", "32.840%"},
|
||||
{"Sharpe Ratio", "7.14272222483913E+15"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "83%"},
|
||||
{"Win Rate", "17%"},
|
||||
{"Profit-Loss Ratio", "13.36"},
|
||||
{"Alpha", "2.59468989671647E+16"},
|
||||
{"Beta", "67.661"},
|
||||
{"Annual Standard Deviation", "3.633"},
|
||||
{"Annual Variance", "13.196"},
|
||||
{"Information Ratio", "7.24987266907741E+15"},
|
||||
{"Tracking Error", "3.579"},
|
||||
{"Treynor Ratio", "383485597312030"},
|
||||
{"Total Fees", "$13.00"},
|
||||
{"Fitness Score", "0.232"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
|
||||
{"Portfolio Turnover", "0.232"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1630141557"}
|
||||
};
|
||||
}
|
||||
}
|
||||
138
Algorithm.CSharp/CustomBuyingPowerModelAlgorithm.cs
Normal file
138
Algorithm.CSharp/CustomBuyingPowerModelAlgorithm.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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 QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Data;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Demonstration of using custom buying power model in backtesting.
|
||||
/// QuantConnect allows you to model all orders as deeply and accurately as you need.
|
||||
/// </summary>
|
||||
/// <meta name="tag" content="trading and orders" />
|
||||
/// <meta name="tag" content="transaction fees and slippage" />
|
||||
/// <meta name="tag" content="custom buying power models" />
|
||||
public class CustomBuyingPowerModelAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _spy;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 01);
|
||||
SetEndDate(2013, 10, 31);
|
||||
var security = AddEquity("SPY", Resolution.Hour);
|
||||
_spy = security.Symbol;
|
||||
|
||||
// set the buying power model
|
||||
security.SetBuyingPowerModel(new CustomBuyingPowerModel());
|
||||
}
|
||||
|
||||
public void OnData(Slice slice)
|
||||
{
|
||||
if (Portfolio.Invested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var quantity = CalculateOrderQuantity(_spy, 1m);
|
||||
if (quantity % 100 != 0)
|
||||
{
|
||||
throw new Exception($"CustomBuyingPowerModel only allow quantity that is multiple of 100 and {quantity} was found");
|
||||
}
|
||||
|
||||
// We normally get insufficient buying power model, but the
|
||||
// CustomBuyingPowerModel always says that there is sufficient buying power for the orders
|
||||
MarketOrder(_spy, quantity * 10);
|
||||
}
|
||||
|
||||
public class CustomBuyingPowerModel : BuyingPowerModel
|
||||
{
|
||||
public override GetMaximumOrderQuantityResult GetMaximumOrderQuantityForTargetBuyingPower(
|
||||
GetMaximumOrderQuantityForTargetBuyingPowerParameters parameters)
|
||||
{
|
||||
var quantity = base.GetMaximumOrderQuantityForTargetBuyingPower(parameters).Quantity;
|
||||
quantity = Math.Floor(quantity / 100) * 100;
|
||||
return new GetMaximumOrderQuantityResult(quantity);
|
||||
}
|
||||
|
||||
public override HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder(
|
||||
HasSufficientBuyingPowerForOrderParameters parameters)
|
||||
{
|
||||
return new HasSufficientBuyingPowerForOrderResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "5672.520%"},
|
||||
{"Drawdown", "22.500%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "40.601%"},
|
||||
{"Sharpe Ratio", "40.201"},
|
||||
{"Probabilistic Sharpe Ratio", "77.339%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "41.848"},
|
||||
{"Beta", "9.224"},
|
||||
{"Annual Standard Deviation", "1.164"},
|
||||
{"Annual Variance", "1.355"},
|
||||
{"Information Ratio", "44.459"},
|
||||
{"Tracking Error", "1.04"},
|
||||
{"Treynor Ratio", "5.073"},
|
||||
{"Total Fees", "$30.00"},
|
||||
{"Fitness Score", "0.418"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "113.05"},
|
||||
{"Return Over Maximum Drawdown", "442.81"},
|
||||
{"Portfolio Turnover", "0.418"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "639761089"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -26,11 +26,12 @@ using QuantConnect.Securities;
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Demonstration of using custom fee, slippage and fill models for modelling transactions in backtesting.
|
||||
/// Demonstration of using custom fee, slippage, fill, and buying power models for modelling transactions in backtesting.
|
||||
/// QuantConnect allows you to model all orders as deeply and accurately as you need.
|
||||
/// </summary>
|
||||
/// <meta name="tag" content="trading and orders" />
|
||||
/// <meta name="tag" content="transaction fees and slippage" />
|
||||
/// <meta name="tag" content="custom buying power models" />
|
||||
/// <meta name="tag" content="custom transaction models" />
|
||||
/// <meta name="tag" content="custom slippage models" />
|
||||
/// <meta name="tag" content="custom fee models" />
|
||||
@@ -50,6 +51,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
_security.SetFeeModel(new CustomFeeModel(this));
|
||||
_security.SetFillModel(new CustomFillModel(this));
|
||||
_security.SetSlippageModel(new CustomSlippageModel(this));
|
||||
_security.SetBuyingPowerModel(new CustomBuyingPowerModel(this));
|
||||
}
|
||||
|
||||
public void OnData(TradeBars data)
|
||||
@@ -60,13 +62,13 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
if (Time.Day > 10 && _security.Holdings.Quantity <= 0)
|
||||
{
|
||||
var quantity = CalculateOrderQuantity(_spy, .5m);
|
||||
Log("MarketOrder: " + quantity);
|
||||
Log($"MarketOrder: {quantity}");
|
||||
MarketOrder(_spy, quantity, asynchronous: true); // async needed for partial fill market orders
|
||||
}
|
||||
else if (Time.Day > 20 && _security.Holdings.Quantity >= 0)
|
||||
{
|
||||
var quantity = CalculateOrderQuantity(_spy, -.5m);
|
||||
Log("MarketOrder: " + quantity);
|
||||
Log($"MarketOrder: {quantity}");
|
||||
MarketOrder(_spy, quantity, asynchronous: true); // async needed for partial fill market orders
|
||||
}
|
||||
}
|
||||
@@ -109,7 +111,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
fill.Status = OrderStatus.PartiallyFilled;
|
||||
}
|
||||
|
||||
_algorithm.Log("CustomFillModel: " + fill);
|
||||
_algorithm.Log($"CustomFillModel: {fill}");
|
||||
|
||||
return fill;
|
||||
}
|
||||
@@ -131,7 +133,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
1m,
|
||||
parameters.Security.Price*parameters.Order.AbsoluteQuantity*0.00001m);
|
||||
|
||||
_algorithm.Log("CustomFeeModel: " + fee);
|
||||
_algorithm.Log($"CustomFeeModel: {fee}");
|
||||
return new OrderFee(new CashAmount(fee, "USD"));
|
||||
}
|
||||
}
|
||||
@@ -150,11 +152,31 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
// custom slippage math
|
||||
var slippage = asset.Price*0.0001m*(decimal) Math.Log10(2*(double) order.AbsoluteQuantity);
|
||||
|
||||
_algorithm.Log("CustomSlippageModel: " + slippage);
|
||||
_algorithm.Log($"CustomSlippageModel: {slippage}");
|
||||
return slippage;
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomBuyingPowerModel : BuyingPowerModel
|
||||
{
|
||||
private readonly QCAlgorithm _algorithm;
|
||||
|
||||
public CustomBuyingPowerModel(QCAlgorithm algorithm)
|
||||
{
|
||||
_algorithm = algorithm;
|
||||
}
|
||||
|
||||
public override HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder(
|
||||
HasSufficientBuyingPowerForOrderParameters parameters)
|
||||
{
|
||||
// custom behavior: this model will assume that there is always enough buying power
|
||||
var hasSufficientBuyingPowerForOrderResult = new HasSufficientBuyingPowerForOrderResult(true);
|
||||
_algorithm.Log($"CustomBuyingPowerModel: {hasSufficientBuyingPowerForOrderResult.IsSufficient}");
|
||||
|
||||
return hasSufficientBuyingPowerForOrderResult;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks that the Tick BidPrice and AskPrices are adjusted like Value.
|
||||
/// </summary>
|
||||
public class EquityTickQuoteAdjustedModeRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _ibm;
|
||||
private bool _bought;
|
||||
private bool _sold;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 7);
|
||||
SetEndDate(2013, 10, 11);
|
||||
SetCash(100000);
|
||||
|
||||
_ibm = AddEquity("IBM", Resolution.Tick).Symbol;
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (!data.Ticks.ContainsKey(_ibm))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var security = Securities[_ibm];
|
||||
if (!security.HasData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var tick in data.Ticks[_ibm])
|
||||
{
|
||||
if (tick.BidPrice != 0 && !_bought && ((tick.Value - tick.BidPrice) <= 0.05m))
|
||||
{
|
||||
SetHoldings(_ibm, 1);
|
||||
_bought = true;
|
||||
return;
|
||||
}
|
||||
if (tick.AskPrice != 0 && _bought && !_sold && Math.Abs((double)tick.Value - (double)tick.AskPrice) <= 0.05)
|
||||
{
|
||||
Liquidate(_ibm);
|
||||
_sold = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "-0.01%"},
|
||||
{"Compounding Annual Return", "-0.500%"},
|
||||
{"Drawdown", "0.000%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Net Profit", "-0.006%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-8.769"},
|
||||
{"Tracking Error", "0.22"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$6.41"},
|
||||
{"Fitness Score", "0.248"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-82.815"},
|
||||
{"Portfolio Turnover", "0.497"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1213851303"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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.Linq;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm testing doing some history requests outside market hours, reproducing GH issue #4783
|
||||
/// </summary>
|
||||
public class ExtendedMarketHoursHistoryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private int _minuteHistoryCount;
|
||||
private int _hourHistoryCount;
|
||||
private int _dailyHistoryCount;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 07);
|
||||
SetEndDate(2013, 10, 09);
|
||||
SetCash(100000);
|
||||
|
||||
AddEquity("SPY", Resolution.Minute, extendedMarketHours:true, fillDataForward:false);
|
||||
|
||||
Schedule.On("RunHistoryCall", DateRules.EveryDay(), TimeRules.Every(TimeSpan.FromHours(1)), RunHistoryCall);
|
||||
}
|
||||
|
||||
private void RunHistoryCall()
|
||||
{
|
||||
var spy = Securities["SPY"];
|
||||
var regularHours = spy.Exchange.Hours.IsOpen(Time, false);
|
||||
var extendedHours = !regularHours && spy.Exchange.Hours.IsOpen(Time, true);
|
||||
|
||||
if (regularHours)
|
||||
{
|
||||
_minuteHistoryCount++;
|
||||
var history = History(spy.Symbol, 5, Resolution.Minute).Count();
|
||||
if (history != 5)
|
||||
{
|
||||
throw new Exception($"Unexpected Minute data count: {history}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (extendedHours)
|
||||
{
|
||||
_hourHistoryCount++;
|
||||
var history = History(spy.Symbol, 5, Resolution.Hour).Count();
|
||||
if (history != 5)
|
||||
{
|
||||
throw new Exception($"Unexpected Hour data count {history}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_dailyHistoryCount++;
|
||||
var history = History(spy.Symbol, 5, Resolution.Daily).Count();
|
||||
if (history != 5)
|
||||
{
|
||||
throw new Exception($"Unexpected Daily data count {history}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
|
||||
/// </summary>
|
||||
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
SetHoldings("SPY", 1);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (_minuteHistoryCount != 3 * 6)
|
||||
{
|
||||
throw new Exception($"Unexpected minute history requests count {_minuteHistoryCount}");
|
||||
}
|
||||
// 6 pre market from 4am to 9am + 4 post market 4pm to 7pm
|
||||
if (_hourHistoryCount != 3 * 10)
|
||||
{
|
||||
throw new Exception($"Unexpected hour history requests count {_hourHistoryCount}");
|
||||
}
|
||||
// 0am to 3am + 8pm to 11pm, last day ends at 8pm
|
||||
if (_dailyHistoryCount != (2 * 8 + 5))
|
||||
{
|
||||
throw new Exception($"Unexpected Daily history requests count: {_dailyHistoryCount}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "20"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0.00%"},
|
||||
{"Compounding Annual Return", "-74.182%"},
|
||||
{"Drawdown", "2.200%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Net Profit", "-1.046%"},
|
||||
{"Sharpe Ratio", "-8.269"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "-0.19"},
|
||||
{"Beta", "0.579"},
|
||||
{"Annual Standard Deviation", "0.065"},
|
||||
{"Annual Variance", "0.004"},
|
||||
{"Information Ratio", "1.326"},
|
||||
{"Tracking Error", "0.049"},
|
||||
{"Treynor Ratio", "-0.934"},
|
||||
{"Total Fees", "$22.26"},
|
||||
{"Fitness Score", "0.002"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-11.855"},
|
||||
{"Return Over Maximum Drawdown", "-70.945"},
|
||||
{"Portfolio Turnover", "0.342"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1961710414"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -141,7 +141,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "699698796"}
|
||||
{"OrderListHash", "1717552327"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1459983342"}
|
||||
{"OrderListHash", "187652813"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,32 +65,32 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "2"},
|
||||
{"Total Trades", "5"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "-0.98%"},
|
||||
{"Compounding Annual Return", "-53.792%"},
|
||||
{"Drawdown", "1.500%"},
|
||||
{"Average Loss", "-0.52%"},
|
||||
{"Compounding Annual Return", "246.602%"},
|
||||
{"Drawdown", "2.300%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Net Profit", "-0.982%"},
|
||||
{"Sharpe Ratio", "-5.949"},
|
||||
{"Probabilistic Sharpe Ratio", "1.216%"},
|
||||
{"Net Profit", "1.602%"},
|
||||
{"Sharpe Ratio", "8.065"},
|
||||
{"Probabilistic Sharpe Ratio", "65.943%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "-0.973"},
|
||||
{"Beta", "0.268"},
|
||||
{"Annual Standard Deviation", "0.077"},
|
||||
{"Annual Variance", "0.006"},
|
||||
{"Information Ratio", "-14.167"},
|
||||
{"Tracking Error", "0.168"},
|
||||
{"Treynor Ratio", "-1.705"},
|
||||
{"Total Fees", "$6.51"},
|
||||
{"Fitness Score", "0.249"},
|
||||
{"Alpha", "-0.157"},
|
||||
{"Beta", "1.015"},
|
||||
{"Annual Standard Deviation", "0.223"},
|
||||
{"Annual Variance", "0.05"},
|
||||
{"Information Ratio", "-27.079"},
|
||||
{"Tracking Error", "0.005"},
|
||||
{"Treynor Ratio", "1.772"},
|
||||
{"Total Fees", "$16.28"},
|
||||
{"Fitness Score", "0.999"},
|
||||
{"Kelly Criterion Estimate", "38.64"},
|
||||
{"Kelly Criterion Probability Value", "0.229"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-55.465"},
|
||||
{"Portfolio Turnover", "0.498"},
|
||||
{"Return Over Maximum Drawdown", "78.607"},
|
||||
{"Portfolio Turnover", "1.246"},
|
||||
{"Total Insights Generated", "100"},
|
||||
{"Total Insights Closed", "99"},
|
||||
{"Total Insights Analysis Completed", "99"},
|
||||
@@ -104,7 +104,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "54.5455%"},
|
||||
{"Rolling Averaged Population Direction", "59.8056%"},
|
||||
{"Rolling Averaged Population Magnitude", "59.8056%"},
|
||||
{"OrderListHash", "160051570"}
|
||||
{"OrderListHash", "-1552239367"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
122
Algorithm.CSharp/OptionAssignmentRegressionAlgorithm.cs
Normal file
122
Algorithm.CSharp/OptionAssignmentRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.Linq;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm verifies automatic option contract assignment behavior.
|
||||
/// </summary>
|
||||
/// <meta name="tag" content="regression test" />
|
||||
/// <meta name="tag" content="options" />
|
||||
/// <meta name="tag" content="using data" />
|
||||
/// <meta name="tag" content="filter selection" />
|
||||
public class OptionAssignmentRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Security Stock;
|
||||
|
||||
private Security CallOption;
|
||||
private Symbol CallOptionSymbol;
|
||||
|
||||
private Security PutOption;
|
||||
private Symbol PutOptionSymbol;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2015, 12, 23);
|
||||
SetEndDate(2015, 12, 24);
|
||||
SetCash(100000);
|
||||
Stock = AddEquity("GOOG", Resolution.Minute);
|
||||
|
||||
var contracts = OptionChainProvider.GetOptionContractList(Stock.Symbol, UtcTime).ToList();
|
||||
|
||||
PutOptionSymbol = contracts
|
||||
.Where(c => c.ID.OptionRight == OptionRight.Put)
|
||||
.OrderBy(c => c.ID.Date)
|
||||
.First(c => c.ID.StrikePrice == 800m);
|
||||
|
||||
CallOptionSymbol = contracts
|
||||
.Where(c => c.ID.OptionRight == OptionRight.Call)
|
||||
.OrderBy(c => c.ID.Date)
|
||||
.First(c => c.ID.StrikePrice == 600m);
|
||||
|
||||
PutOption = AddOptionContract(PutOptionSymbol);
|
||||
CallOption = AddOptionContract(CallOptionSymbol);
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (!Portfolio.Invested && Stock.Price != 0 && PutOption.Price != 0 && CallOption.Price != 0)
|
||||
{
|
||||
// this gets executed on start and after each auto-assignment, finally ending with expiration assignment
|
||||
MarketOrder(PutOptionSymbol, -1);
|
||||
MarketOrder(CallOptionSymbol, -1);
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanRunLocally { get; } = true;
|
||||
public Language[] Languages { get; } = {Language.CSharp};
|
||||
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "22"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$12.00"},
|
||||
{"Fitness Score", "0.5"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-50.218"},
|
||||
{"Portfolio Turnover", "6.713"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1597098916"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ using QuantConnect.Interfaces;
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Demonstration of the Option Chain Provider -- a much faster mechanism for manually specifying the option contracts you'd like to recieve
|
||||
/// Demonstration of the Option Chain Provider -- a much faster mechanism for manually specifying the option contracts you'd like to receive
|
||||
/// data for and manually subscribing to them.
|
||||
/// </summary>
|
||||
/// <meta name="tag" content="strategy example" />
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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.Linq;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Algorithm.Framework.Selection;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm making sure that the added universe selection does not remove the option chain during it's daily refresh
|
||||
/// </summary>
|
||||
public class OptionChainedAndUniverseSelectionRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _aaplOption;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
UniverseSettings.Resolution = Resolution.Minute;
|
||||
|
||||
SetStartDate(2014, 06, 05);
|
||||
SetEndDate(2014, 06, 09);
|
||||
|
||||
_aaplOption = AddOption("AAPL").Symbol;
|
||||
AddUniverseSelection(new DailyUniverseSelectionModel("MyCustomSelectionModel", time => new[] { "AAPL" }, this));
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
Buy("AAPL", 1);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
var config = SubscriptionManager.Subscriptions.ToList();
|
||||
if (config.All(dataConfig => dataConfig.Symbol != "AAPL"))
|
||||
{
|
||||
throw new Exception("Was expecting configurations for AAPL");
|
||||
}
|
||||
if (config.All(dataConfig => dataConfig.Symbol.SecurityType != SecurityType.Option))
|
||||
{
|
||||
throw new Exception($"Was expecting configurations for {_aaplOption}");
|
||||
}
|
||||
}
|
||||
|
||||
private class DailyUniverseSelectionModel : CustomUniverseSelectionModel
|
||||
{
|
||||
private DateTime _lastRefresh;
|
||||
private IAlgorithm _algorithm;
|
||||
|
||||
public DailyUniverseSelectionModel(string name, Func<DateTime, IEnumerable<string>> selector, IAlgorithm algorithm) : base(name, selector)
|
||||
{
|
||||
_algorithm = algorithm;
|
||||
}
|
||||
|
||||
public override DateTime GetNextRefreshTimeUtc()
|
||||
{
|
||||
if (_lastRefresh != _algorithm.Time.Date)
|
||||
{
|
||||
_lastRefresh = _algorithm.Time.Date;
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
return DateTime.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0.678%"},
|
||||
{"Drawdown", "3.700%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0.009%"},
|
||||
{"Sharpe Ratio", "7.969"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.046"},
|
||||
{"Beta", "-0.032"},
|
||||
{"Annual Standard Deviation", "0.001"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-24.461"},
|
||||
{"Tracking Error", "0.044"},
|
||||
{"Treynor Ratio", "-0.336"},
|
||||
{"Total Fees", "$1.00"},
|
||||
{"Fitness Score", "0.003"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
|
||||
{"Portfolio Turnover", "0.003"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1779427412"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1038273097"}
|
||||
{"OrderListHash", "-1726463684"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,8 +142,17 @@
|
||||
<Link>Properties\SharedAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AddAlphaModelAlgorithm.cs" />
|
||||
<Compile Include="CustomBuyingPowerModelAlgorithm.cs" />
|
||||
<Compile Include="AddOptionContractExpiresRegressionAlgorithm.cs" />
|
||||
<Compile Include="ScaledFillForwardDataRegressionAlgorithm.cs" />
|
||||
<Compile Include="DailyHistoryForDailyResolutionRegressionAlgorithm.cs" />
|
||||
<Compile Include="DailyHistoryForMinuteResolutionRegressionAlgorithm.cs" />
|
||||
<Compile Include="ExtendedMarketHoursHistoryRegressionAlgorithm.cs" />
|
||||
<Compile Include="EquityTickQuoteAdjustedModeRegressionAlgorithm.cs" />
|
||||
<Compile Include="AddOptionContractFromUniverseRegressionAlgorithm.cs" />
|
||||
<Compile Include="CoarseFineOptionUniverseChainRegressionAlgorithm.cs" />
|
||||
<Compile Include="OptionChainedAndUniverseSelectionRegressionAlgorithm.cs" />
|
||||
<Compile Include="OptionAssignmentRegressionAlgorithm.cs" />
|
||||
<Compile Include="SwitchDataModeRegressionAlgorithm.cs" />
|
||||
<Compile Include="AddRemoveOptionUniverseRegressionAlgorithm.cs" />
|
||||
<Compile Include="AddRemoveSecurityRegressionAlgorithm.cs" />
|
||||
|
||||
143
Algorithm.CSharp/ScaledFillForwardDataRegressionAlgorithm.cs
Normal file
143
Algorithm.CSharp/ScaledFillForwardDataRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Interfaces;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression test algorithm reproduces issue https://github.com/QuantConnect/Lean/issues/4834
|
||||
/// fixed in PR https://github.com/QuantConnect/Lean/pull/4836
|
||||
/// Adjusted data of fill forward bars should use original scale factor
|
||||
/// </summary>
|
||||
public class ScaledFillForwardDataRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private TradeBar _lastRealBar;
|
||||
private Symbol _twx;
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2014, 6, 5);
|
||||
SetEndDate(2014, 6, 9);
|
||||
|
||||
_twx = AddEquity("TWX", Resolution.Minute, extendedMarketHours: true).Symbol;
|
||||
Schedule.On(DateRules.EveryDay(_twx), TimeRules.Every(TimeSpan.FromHours(1)), PlotPrice);
|
||||
}
|
||||
|
||||
private void PlotPrice()
|
||||
{
|
||||
Plot($"{_twx}", "Ask", Securities[_twx].AskPrice);
|
||||
Plot($"{_twx}", "Bid", Securities[_twx].BidPrice);
|
||||
Plot($"{_twx}", "Price", Securities[_twx].Price);
|
||||
Plot("Portfolio.TPV", "Value", Portfolio.TotalPortfolioValue);
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
var current = data.Bars.FirstOrDefault().Value;
|
||||
if (current != null)
|
||||
{
|
||||
if (Time == new DateTime(2014, 06, 09, 4, 1, 0) && !Portfolio.Invested)
|
||||
{
|
||||
if (!current.IsFillForward)
|
||||
{
|
||||
throw new Exception($"Was expecting a first fill forward bar {Time}");
|
||||
}
|
||||
|
||||
// trade on the first bar after a factor price scale change. +10 so we fill ASAP. Limit so it fills in extended market hours
|
||||
LimitOrder(_twx, 1000, _lastRealBar.Close + 10);
|
||||
}
|
||||
|
||||
if (_lastRealBar == null || !current.IsFillForward)
|
||||
{
|
||||
_lastRealBar = current;
|
||||
}
|
||||
else if (_lastRealBar.Close != current.Close)
|
||||
{
|
||||
throw new Exception($"FillForwarded data point at {Time} was scaled. Actual: {current.Close}; Expected: {_lastRealBar.Close}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (_lastRealBar == null)
|
||||
{
|
||||
throw new Exception($"Not all expected data points were received.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "32.825%"},
|
||||
{"Drawdown", "0.800%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0.377%"},
|
||||
{"Sharpe Ratio", "8.953"},
|
||||
{"Probabilistic Sharpe Ratio", "95.977%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.314"},
|
||||
{"Beta", "-0.104"},
|
||||
{"Annual Standard Deviation", "0.03"},
|
||||
{"Annual Variance", "0.001"},
|
||||
{"Information Ratio", "-3.498"},
|
||||
{"Tracking Error", "0.05"},
|
||||
{"Treynor Ratio", "-2.573"},
|
||||
{"Total Fees", "$5.00"},
|
||||
{"Fitness Score", "0.158"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
|
||||
{"Portfolio Turnover", "0.158"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "960108217"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
if (_expectedCloseValues.Count > 0)
|
||||
{
|
||||
throw new Exception($"Not all expected data points were recieved.");
|
||||
throw new Exception($"Not all expected data points were received.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-569072921"}
|
||||
{"OrderListHash", "359885308"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,32 +172,32 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "5"},
|
||||
{"Total Trades", "4"},
|
||||
{"Average Win", "0.64%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "-74.197%"},
|
||||
{"Drawdown", "6.600%"},
|
||||
{"Compounding Annual Return", "-56.577%"},
|
||||
{"Drawdown", "3.800%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "-6.115%"},
|
||||
{"Sharpe Ratio", "-2.281"},
|
||||
{"Probabilistic Sharpe Ratio", "11.870%"},
|
||||
{"Net Profit", "-3.811%"},
|
||||
{"Sharpe Ratio", "-2.773"},
|
||||
{"Probabilistic Sharpe Ratio", "13.961%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "100%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "-0.684"},
|
||||
{"Beta", "-0.113"},
|
||||
{"Annual Standard Deviation", "0.292"},
|
||||
{"Annual Variance", "0.085"},
|
||||
{"Information Ratio", "-1.606"},
|
||||
{"Tracking Error", "0.312"},
|
||||
{"Treynor Ratio", "5.866"},
|
||||
{"Total Fees", "$5.00"},
|
||||
{"Fitness Score", "0.017"},
|
||||
{"Alpha", "-0.504"},
|
||||
{"Beta", "-0.052"},
|
||||
{"Annual Standard Deviation", "0.179"},
|
||||
{"Annual Variance", "0.032"},
|
||||
{"Information Ratio", "-1.599"},
|
||||
{"Tracking Error", "0.207"},
|
||||
{"Treynor Ratio", "9.508"},
|
||||
{"Total Fees", "$4.00"},
|
||||
{"Fitness Score", "0.008"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-2.584"},
|
||||
{"Return Over Maximum Drawdown", "-11.287"},
|
||||
{"Portfolio Turnover", "0.177"},
|
||||
{"Sortino Ratio", "-3.791"},
|
||||
{"Return Over Maximum Drawdown", "-14.846"},
|
||||
{"Portfolio Turnover", "0.136"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
@@ -211,7 +211,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1386253041"}
|
||||
{"OrderListHash", "1484950465"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,9 +67,11 @@ namespace QuantConnect.Algorithm.Framework.Risk
|
||||
}
|
||||
|
||||
var pnl = GetTotalDrawdownPercent(currentValue);
|
||||
if (pnl < _maximumDrawdownPercent)
|
||||
if (pnl < _maximumDrawdownPercent && targets.Length != 0)
|
||||
{
|
||||
foreach(var target in targets)
|
||||
// reset the trailing high value for restart investing on next rebalcing period
|
||||
_initialised = false;
|
||||
foreach (var target in targets)
|
||||
yield return new PortfolioTarget(target.Symbol, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,10 +54,11 @@ class MaximumDrawdownPercentPortfolio(RiskManagementModel):
|
||||
return [] # return if new high reached
|
||||
|
||||
pnl = self.GetTotalDrawdownPercent(currentValue)
|
||||
if pnl < self.maximumDrawdownPercent:
|
||||
if pnl < self.maximumDrawdownPercent and len(targets) != 0:
|
||||
self.initialised = False # reset the trailing high value for restart investing on next rebalcing period
|
||||
return [ PortfolioTarget(target.Symbol, 0) for target in targets ]
|
||||
|
||||
return []
|
||||
|
||||
def GetTotalDrawdownPercent(self, currentValue):
|
||||
return (float(currentValue) / float(self.portfolioHigh)) - 1.0
|
||||
return (float(currentValue) / float(self.portfolioHigh)) - 1.0
|
||||
|
||||
@@ -15,12 +15,9 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Auxiliary;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Option;
|
||||
|
||||
namespace QuantConnect.Algorithm.Framework.Selection
|
||||
{
|
||||
@@ -107,53 +104,11 @@ namespace QuantConnect.Algorithm.Framework.Selection
|
||||
// prevent creating duplicate option chains -- one per underlying
|
||||
if (uniqueUnderlyingSymbols.Add(optionSymbol.Underlying))
|
||||
{
|
||||
yield return CreateOptionChain(algorithm, optionSymbol);
|
||||
yield return algorithm.CreateOptionChain(optionSymbol, Filter, _universeSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the canonical <see cref="Option"/> chain security for a given symbol
|
||||
/// </summary>
|
||||
/// <param name="algorithm">The algorithm instance to create universes for</param>
|
||||
/// <param name="symbol">Symbol of the option</param>
|
||||
/// <param name="settings">Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed</param>
|
||||
/// <param name="initializer">Performs extra initialization (such as setting models) after we create a new security object</param>
|
||||
/// <returns><see cref="Option"/> for the given symbol</returns>
|
||||
[Obsolete("This method is obsolete because SecurityInitializer is obsolete and will not be used.")]
|
||||
protected virtual Option CreateOptionChainSecurity(QCAlgorithm algorithm, Symbol symbol, UniverseSettings settings, ISecurityInitializer initializer)
|
||||
{
|
||||
return CreateOptionChainSecurity(
|
||||
algorithm.SubscriptionManager.SubscriptionDataConfigService,
|
||||
symbol,
|
||||
settings,
|
||||
algorithm.Securities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the canonical <see cref="Option"/> chain security for a given symbol
|
||||
/// </summary>
|
||||
/// <param name="subscriptionDataConfigService">The service used to create new <see cref="SubscriptionDataConfig"/></param>
|
||||
/// <param name="symbol">Symbol of the option</param>
|
||||
/// <param name="settings">Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed</param>
|
||||
/// <param name="securityManager">Used to create new <see cref="Security"/></param>
|
||||
/// <returns><see cref="Option"/> for the given symbol</returns>
|
||||
protected virtual Option CreateOptionChainSecurity(
|
||||
ISubscriptionDataConfigService subscriptionDataConfigService,
|
||||
Symbol symbol,
|
||||
UniverseSettings settings,
|
||||
SecurityManager securityManager)
|
||||
{
|
||||
var config = subscriptionDataConfigService.Add(
|
||||
typeof(ZipEntryName),
|
||||
symbol,
|
||||
settings.Resolution,
|
||||
settings.FillForward,
|
||||
settings.ExtendedMarketHours,
|
||||
false);
|
||||
return (Option)securityManager.CreateSecurity(symbol, config, settings.Leverage, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the option chain universe filter
|
||||
/// </summary>
|
||||
@@ -162,55 +117,5 @@ namespace QuantConnect.Algorithm.Framework.Selection
|
||||
// NOP
|
||||
return filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="OptionChainUniverse"/> for a given symbol
|
||||
/// </summary>
|
||||
/// <param name="algorithm">The algorithm instance to create universes for</param>
|
||||
/// <param name="symbol">Symbol of the option</param>
|
||||
/// <returns><see cref="OptionChainUniverse"/> for the given symbol</returns>
|
||||
private OptionChainUniverse CreateOptionChain(QCAlgorithm algorithm, Symbol symbol)
|
||||
{
|
||||
if (symbol.SecurityType != SecurityType.Option)
|
||||
{
|
||||
throw new ArgumentException("CreateOptionChain requires an option symbol.");
|
||||
}
|
||||
|
||||
// rewrite non-canonical symbols to be canonical
|
||||
var market = symbol.ID.Market;
|
||||
var underlying = symbol.Underlying;
|
||||
if (!symbol.IsCanonical())
|
||||
{
|
||||
var alias = $"?{underlying.Value}";
|
||||
symbol = Symbol.Create(underlying.Value, SecurityType.Option, market, alias);
|
||||
}
|
||||
|
||||
// resolve defaults if not specified
|
||||
var settings = _universeSettings ?? algorithm.UniverseSettings;
|
||||
|
||||
// create canonical security object, but don't duplicate if it already exists
|
||||
Security security;
|
||||
Option optionChain;
|
||||
if (!algorithm.Securities.TryGetValue(symbol, out security))
|
||||
{
|
||||
optionChain = CreateOptionChainSecurity(
|
||||
algorithm.SubscriptionManager.SubscriptionDataConfigService,
|
||||
symbol,
|
||||
settings,
|
||||
algorithm.Securities);
|
||||
}
|
||||
else
|
||||
{
|
||||
optionChain = (Option)security;
|
||||
}
|
||||
|
||||
// set the option chain contract filter function
|
||||
optionChain.SetFilter(Filter);
|
||||
|
||||
// force option chain security to not be directly tradable AFTER it's configured to ensure it's not overwritten
|
||||
optionChain.IsTradable = false;
|
||||
|
||||
return new OptionChainUniverse(optionChain, settings, algorithm.LiveMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
# 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.
|
||||
|
||||
from clr import AddReference
|
||||
AddReference("System")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
AddReference("QuantConnect.Common")
|
||||
|
||||
from System import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from datetime import *
|
||||
|
||||
### <summary>
|
||||
### We add an option contract using 'QCAlgorithm.AddOptionContract' and place a trade, the underlying
|
||||
### gets deselected from the universe selection but should still be present since we manually added the option contract.
|
||||
### Later we call 'QCAlgorithm.RemoveOptionContract' and expect both option and underlying to be removed.
|
||||
### </summary>
|
||||
class AddOptionContractExpiresRegressionAlgorithm(QCAlgorithm):
|
||||
def Initialize(self):
|
||||
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
|
||||
|
||||
self.SetStartDate(2014, 6, 5)
|
||||
self.SetEndDate(2014, 6, 30)
|
||||
|
||||
self._expiration = datetime(2014, 6, 21)
|
||||
self._option = None
|
||||
self._traded = False
|
||||
|
||||
self._twx = Symbol.Create("TWX", SecurityType.Equity, Market.USA)
|
||||
|
||||
self.AddUniverse("my-daily-universe-name", self.Selector)
|
||||
|
||||
def Selector(self, time):
|
||||
return [ "AAPL" ]
|
||||
|
||||
def OnData(self, data):
|
||||
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
|
||||
|
||||
Arguments:
|
||||
data: Slice object keyed by symbol containing the stock data
|
||||
'''
|
||||
if self._option == None:
|
||||
options = self.OptionChainProvider.GetOptionContractList(self._twx, self.Time)
|
||||
options = sorted(options, key=lambda x: x.ID.Symbol)
|
||||
|
||||
option = next((option for option in options if option.ID.Date == self._expiration and option.ID.OptionRight == OptionRight.Call and option.ID.OptionStyle == OptionStyle.American), None)
|
||||
if option != None:
|
||||
self._option = self.AddOptionContract(option).Symbol;
|
||||
|
||||
if self._option != None and self.Securities[self._option].Price != 0 and not self._traded:
|
||||
self._traded = True;
|
||||
self.Buy(self._option, 1);
|
||||
|
||||
if self.Time > self._expiration and self.Securities[self._twx].Invested:
|
||||
# we liquidate the option exercised position
|
||||
self.Liquidate(self._twx);
|
||||
@@ -0,0 +1,87 @@
|
||||
# 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.
|
||||
|
||||
from clr import AddReference
|
||||
AddReference("System")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
AddReference("QuantConnect.Common")
|
||||
|
||||
from System import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from datetime import *
|
||||
|
||||
### <summary>
|
||||
### We add an option contract using 'QCAlgorithm.AddOptionContract' and place a trade, the underlying
|
||||
### gets deselected from the universe selection but should still be present since we manually added the option contract.
|
||||
### Later we call 'QCAlgorithm.RemoveOptionContract' and expect both option and underlying to be removed.
|
||||
### </summary>
|
||||
class AddOptionContractFromUniverseRegressionAlgorithm(QCAlgorithm):
|
||||
def Initialize(self):
|
||||
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
|
||||
|
||||
self.SetStartDate(2014, 6, 5)
|
||||
self.SetEndDate(2014, 6, 9)
|
||||
|
||||
self._expiration = datetime(2014, 6, 21)
|
||||
self._securityChanges = None
|
||||
self._option = None
|
||||
self._traded = False
|
||||
|
||||
self._twx = Symbol.Create("TWX", SecurityType.Equity, Market.USA)
|
||||
self._aapl = Symbol.Create("AAPL", SecurityType.Equity, Market.USA)
|
||||
self.UniverseSettings.Resolution = Resolution.Minute
|
||||
self.UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw
|
||||
|
||||
self.AddUniverse(self.Selector, self.Selector)
|
||||
|
||||
def Selector(self, fundamental):
|
||||
if self.Time <= datetime(2014, 6, 5):
|
||||
return [ self._twx ]
|
||||
return [ self._aapl ]
|
||||
|
||||
def OnData(self, data):
|
||||
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
|
||||
|
||||
Arguments:
|
||||
data: Slice object keyed by symbol containing the stock data
|
||||
'''
|
||||
if self._option != None and self.Securities[self._option].Price != 0 and not self._traded:
|
||||
self._traded = True;
|
||||
self.Buy(self._option, 1);
|
||||
|
||||
if self.Time == datetime(2014, 6, 6, 14, 0, 0):
|
||||
# liquidate & remove the option
|
||||
self.RemoveOptionContract(self._option)
|
||||
|
||||
def OnSecuritiesChanged(self, changes):
|
||||
# keep track of all removed and added securities
|
||||
if self._securityChanges == None:
|
||||
self._securityChanges = changes
|
||||
else:
|
||||
self._securityChanges.op_Addition(self._securityChanges, changes)
|
||||
|
||||
if any(security.Symbol.SecurityType == SecurityType.Option for security in changes.AddedSecurities):
|
||||
return
|
||||
|
||||
for addedSecurity in changes.AddedSecurities:
|
||||
options = self.OptionChainProvider.GetOptionContractList(addedSecurity.Symbol, self.Time)
|
||||
options = sorted(options, key=lambda x: x.ID.Symbol)
|
||||
|
||||
option = next((option for option in options if option.ID.Date == self._expiration and option.ID.OptionRight == OptionRight.Call and option.ID.OptionStyle == OptionStyle.American), None)
|
||||
|
||||
self.AddOptionContract(option)
|
||||
|
||||
# just keep the first we got
|
||||
if self._option == None:
|
||||
self._option = option
|
||||
@@ -0,0 +1,99 @@
|
||||
# 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.
|
||||
|
||||
from clr import AddReference
|
||||
AddReference("System.Core")
|
||||
AddReference("System.Collections")
|
||||
AddReference("QuantConnect.Common")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
|
||||
from System import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data.UniverseSelection import *
|
||||
from datetime import *
|
||||
|
||||
### <summary>
|
||||
### Demonstration of how to chain a coarse and fine universe selection with an option chain universe selection model
|
||||
### that will add and remove an'OptionChainUniverse' for each symbol selected on fine
|
||||
### </summary>
|
||||
class CoarseFineOptionUniverseChainRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
def Initialize(self):
|
||||
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
|
||||
|
||||
self.SetStartDate(2014,6,5) #Set Start Date
|
||||
self.SetEndDate(2014,6,6) #Set End Date
|
||||
|
||||
self.UniverseSettings.Resolution = Resolution.Minute
|
||||
self._twx = Symbol.Create("TWX", SecurityType.Equity, Market.USA)
|
||||
self._aapl = Symbol.Create("AAPL", SecurityType.Equity, Market.USA)
|
||||
self._lastEquityAdded = None
|
||||
self._changes = None
|
||||
self._optionCount = 0
|
||||
|
||||
universe = self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
|
||||
|
||||
self.AddUniverseOptions(universe, self.OptionFilterFunction)
|
||||
|
||||
def OptionFilterFunction(self, universe):
|
||||
universe.IncludeWeeklys().FrontMonth()
|
||||
|
||||
contracts = list()
|
||||
for symbol in universe:
|
||||
if len(contracts) == 5:
|
||||
break
|
||||
contracts.append(symbol)
|
||||
return universe.Contracts(contracts)
|
||||
|
||||
def CoarseSelectionFunction(self, coarse):
|
||||
if self.Time <= datetime(2014,6,5):
|
||||
return [ self._twx ]
|
||||
return [ self._aapl ]
|
||||
|
||||
def FineSelectionFunction(self, fine):
|
||||
if self.Time <= datetime(2014,6,5):
|
||||
return [ self._twx ]
|
||||
return [ self._aapl ]
|
||||
|
||||
def OnData(self, data):
|
||||
if self._changes == None or any(security.Price == 0 for security in self._changes.AddedSecurities):
|
||||
return
|
||||
|
||||
# liquidate removed securities
|
||||
for security in self._changes.RemovedSecurities:
|
||||
if security.Invested:
|
||||
self.Liquidate(security.Symbol);
|
||||
|
||||
for security in self._changes.AddedSecurities:
|
||||
if not security.Symbol.HasUnderlying:
|
||||
self._lastEquityAdded = security.Symbol;
|
||||
else:
|
||||
# options added should all match prev added security
|
||||
if security.Symbol.Underlying != self._lastEquityAdded:
|
||||
raise ValueError(f"Unexpected symbol added {security.Symbol}")
|
||||
self._optionCount += 1
|
||||
|
||||
self.SetHoldings(security.Symbol, 0.05)
|
||||
self._changes = None
|
||||
|
||||
# this event fires whenever we have changes to our universe
|
||||
def OnSecuritiesChanged(self, changes):
|
||||
if self._changes == None:
|
||||
self._changes = changes
|
||||
return
|
||||
self._changes = self._changes.op_Addition(self._changes, changes)
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
if self._optionCount == 0:
|
||||
raise ValueError("Option universe chain did not add any option!")
|
||||
@@ -22,7 +22,6 @@ from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Indicators import *
|
||||
from QuantConnect.Securities import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Data.Consolidators import *
|
||||
from CustomDataRegressionAlgorithm import Bitcoin
|
||||
from datetime import timedelta
|
||||
|
||||
65
Algorithm.Python/CustomBuyingPowerModelAlgorithm.py
Normal file
65
Algorithm.Python/CustomBuyingPowerModelAlgorithm.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# 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.
|
||||
|
||||
from clr import AddReference
|
||||
AddReference("System")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
AddReference("QuantConnect.Common")
|
||||
|
||||
from System import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Securities import *
|
||||
import numpy as np
|
||||
|
||||
### <summary>
|
||||
### Demonstration of using custom buying power model in backtesting.
|
||||
### QuantConnect allows you to model all orders as deeply and accurately as you need.
|
||||
### </summary>
|
||||
### <meta name="tag" content="trading and orders" />
|
||||
### <meta name="tag" content="transaction fees and slippage" />
|
||||
### <meta name="tag" content="custom buying power models" />
|
||||
class CustomBuyingPowerModelAlgorithm(QCAlgorithm):
|
||||
'''Demonstration of using custom buying power model in backtesting.
|
||||
QuantConnect allows you to model all orders as deeply and accurately as you need.'''
|
||||
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2013,10,1) # Set Start Date
|
||||
self.SetEndDate(2013,10,31) # Set End Date
|
||||
security = self.AddEquity("SPY", Resolution.Hour)
|
||||
self.spy = security.Symbol
|
||||
|
||||
# set the buying power model
|
||||
security.SetBuyingPowerModel(CustomBuyingPowerModel())
|
||||
|
||||
def OnData(self, slice):
|
||||
if self.Portfolio.Invested:
|
||||
return
|
||||
|
||||
quantity = self.CalculateOrderQuantity(self.spy, 1)
|
||||
if quantity % 100 != 0:
|
||||
raise Exception(f'CustomBuyingPowerModel only allow quantity that is multiple of 100 and {quantity} was found')
|
||||
|
||||
# We normally get insufficient buying power model, but the
|
||||
# CustomBuyingPowerModel always says that there is sufficient buying power for the orders
|
||||
self.MarketOrder(self.spy, quantity * 10)
|
||||
|
||||
|
||||
class CustomBuyingPowerModel(BuyingPowerModel):
|
||||
def GetMaximumOrderQuantityForTargetBuyingPower(self, parameters):
|
||||
quantity = super().GetMaximumOrderQuantityForTargetBuyingPower(parameters).Quantity
|
||||
quantity = np.floor(quantity / 100) * 100
|
||||
return GetMaximumOrderQuantityResult(quantity)
|
||||
|
||||
def HasSufficientBuyingPowerForOrder(self, parameters):
|
||||
return HasSufficientBuyingPowerForOrderResult(True)
|
||||
@@ -13,19 +13,18 @@
|
||||
|
||||
from clr import AddReference
|
||||
AddReference("System")
|
||||
AddReference("QuantConnect.Common")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
AddReference("QuantConnect.Indicators")
|
||||
AddReference("QuantConnect.Common")
|
||||
|
||||
from System import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Python import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Algorithm.Framework.Selection import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Data.Consolidators import *
|
||||
from QuantConnect.Indicators import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from System import *
|
||||
from datetime import *
|
||||
|
||||
class CustomConsolidatorRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
@@ -21,6 +21,7 @@ from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Algorithm.Framework.Selection import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Custom import *
|
||||
from QuantConnect.Data.Custom.SEC import *
|
||||
from QuantConnect.Data.UniverseSelection import *
|
||||
|
||||
|
||||
@@ -27,16 +27,17 @@ import numpy as np
|
||||
import random
|
||||
|
||||
### <summary>
|
||||
### Demonstration of using custom fee, slippage and fill models for modelling transactions in backtesting.
|
||||
### Demonstration of using custom fee, slippage, fill, and buying power models for modelling transactions in backtesting.
|
||||
### QuantConnect allows you to model all orders as deeply and accurately as you need.
|
||||
### </summary>
|
||||
### <meta name="tag" content="trading and orders" />
|
||||
### <meta name="tag" content="transaction fees and slippage" />
|
||||
### <meta name="tag" content="custom buying power models" />
|
||||
### <meta name="tag" content="custom transaction models" />
|
||||
### <meta name="tag" content="custom slippage models" />
|
||||
### <meta name="tag" content="custom fee models" />
|
||||
class CustomModelsAlgorithm(QCAlgorithm):
|
||||
'''Demonstration of using custom fee, slippage and fill models for modelling transactions in backtesting.
|
||||
'''Demonstration of using custom fee, slippage, fill, and buying power models for modelling transactions in backtesting.
|
||||
QuantConnect allows you to model all orders as deeply and accurately as you need.'''
|
||||
|
||||
def Initialize(self):
|
||||
@@ -49,6 +50,7 @@ class CustomModelsAlgorithm(QCAlgorithm):
|
||||
self.security.SetFeeModel(CustomFeeModel(self))
|
||||
self.security.SetFillModel(CustomFillModel(self))
|
||||
self.security.SetSlippageModel(CustomSlippageModel(self))
|
||||
self.security.SetBuyingPowerModel(CustomBuyingPowerModel(self))
|
||||
|
||||
|
||||
def OnData(self, data):
|
||||
@@ -57,12 +59,12 @@ class CustomModelsAlgorithm(QCAlgorithm):
|
||||
|
||||
if self.Time.day > 10 and self.security.Holdings.Quantity <= 0:
|
||||
quantity = self.CalculateOrderQuantity(self.spy, .5)
|
||||
self.Log("MarketOrder: " + str(quantity))
|
||||
self.Log(f"MarketOrder: {quantity}")
|
||||
self.MarketOrder(self.spy, quantity, True) # async needed for partial fill market orders
|
||||
|
||||
elif self.Time.day > 20 and self.security.Holdings.Quantity >= 0:
|
||||
quantity = self.CalculateOrderQuantity(self.spy, -.5)
|
||||
self.Log("MarketOrder: " + str(quantity))
|
||||
self.Log(f"MarketOrder: {quantity}")
|
||||
self.MarketOrder(self.spy, quantity, True) # async needed for partial fill market orders
|
||||
|
||||
# If we want to use methods from other models, you need to inherit from one of them
|
||||
@@ -90,7 +92,7 @@ class CustomFillModel(ImmediateFillModel):
|
||||
absoluteRemaining = absoluteRemaining - absoluteFillQuantity
|
||||
self.absoluteRemainingByOrderId[order.Id] = absoluteRemaining
|
||||
fill.Status = OrderStatus.PartiallyFilled
|
||||
self.algorithm.Log("CustomFillModel: " + str(fill))
|
||||
self.algorithm.Log(f"CustomFillModel: {fill}")
|
||||
return fill
|
||||
|
||||
class CustomFeeModel(FeeModel):
|
||||
@@ -102,7 +104,7 @@ class CustomFeeModel(FeeModel):
|
||||
fee = max(1, parameters.Security.Price
|
||||
* parameters.Order.AbsoluteQuantity
|
||||
* 0.00001)
|
||||
self.algorithm.Log("CustomFeeModel: " + str(fee))
|
||||
self.algorithm.Log(f"CustomFeeModel: {fee}")
|
||||
return OrderFee(CashAmount(fee, "USD"))
|
||||
|
||||
class CustomSlippageModel:
|
||||
@@ -112,5 +114,15 @@ class CustomSlippageModel:
|
||||
def GetSlippageApproximation(self, asset, order):
|
||||
# custom slippage math
|
||||
slippage = asset.Price * 0.0001 * np.log10(2*float(order.AbsoluteQuantity))
|
||||
self.algorithm.Log("CustomSlippageModel: " + str(slippage))
|
||||
return slippage
|
||||
self.algorithm.Log(f"CustomSlippageModel: {slippage}")
|
||||
return slippage
|
||||
|
||||
class CustomBuyingPowerModel(BuyingPowerModel):
|
||||
def __init__(self, algorithm):
|
||||
self.algorithm = algorithm
|
||||
|
||||
def HasSufficientBuyingPowerForOrder(self, parameters):
|
||||
# custom behavior: this model will assume that there is always enough buying power
|
||||
hasSufficientBuyingPowerForOrderResult = HasSufficientBuyingPowerForOrderResult(True)
|
||||
self.algorithm.Log(f"CustomBuyingPowerModel: {hasSufficientBuyingPowerForOrderResult.IsSufficient}")
|
||||
return hasSufficientBuyingPowerForOrderResult
|
||||
@@ -12,15 +12,15 @@
|
||||
# limitations under the License.
|
||||
|
||||
from clr import AddReference
|
||||
AddReference("System.Core")
|
||||
AddReference("QuantConnect.Common")
|
||||
AddReference("System")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
AddReference("QuantConnect.Algorithm.Framework")
|
||||
AddReference("QuantConnect.Common")
|
||||
|
||||
from System import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import QCAlgorithm
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Algorithm.Framework.Selection import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.UniverseSelection import *
|
||||
from datetime import timedelta
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
from clr import AddReference
|
||||
AddReference("System")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
AddReference("QuantConnect.Algorithm.Framework")
|
||||
AddReference("QuantConnect.Common")
|
||||
|
||||
from QuantConnect import *
|
||||
@@ -24,7 +23,6 @@ from QuantConnect.Algorithm.Framework.Execution import *
|
||||
from QuantConnect.Algorithm.Framework.Portfolio import *
|
||||
from QuantConnect.Algorithm.Framework.Selection import *
|
||||
from QuantConnect.Brokerages import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Interfaces import *
|
||||
from QuantConnect.Orders import *
|
||||
from System import *
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
<ItemGroup>
|
||||
<Content Include="AccumulativeInsightPortfolioRegressionAlgorithm.py" />
|
||||
<Content Include="AddAlphaModelAlgorithm.py" />
|
||||
<Content Include="AddOptionContractExpiresRegressionAlgorithm.py" />
|
||||
<Content Include="AddOptionContractFromUniverseRegressionAlgorithm.py" />
|
||||
<Content Include="AddRiskManagementAlgorithm.py" />
|
||||
<Content Include="AddUniverseSelectionModelAlgorithm.py" />
|
||||
<Content Include="Alphas\ContingentClaimsAnalysisDefaultPredictionAlpha.py" />
|
||||
@@ -83,11 +85,13 @@
|
||||
<None Include="BasicTemplateOptionsPriceModel.py" />
|
||||
<Content Include="Benchmarks\SECReportBenchmarkAlgorithm.py" />
|
||||
<Content Include="Benchmarks\SmartInsiderEventBenchmarkAlgorithm.py" />
|
||||
<Content Include="CoarseFineOptionUniverseChainRegressionAlgorithm.py" />
|
||||
<Content Include="CoarseTiingoNewsUniverseSelectionAlgorithm.py" />
|
||||
<Content Include="ConsolidateRegressionAlgorithm.py" />
|
||||
<Content Include="CustomConsolidatorRegressionAlgorithm.py" />
|
||||
<Content Include="CustomDataAddDataOnSecuritiesChangedRegressionAlgorithm.py" />
|
||||
<Content Include="CustomDataAddDataCoarseSelectionRegressionAlgorithm.py" />
|
||||
<None Include="CustomBuyingPowerModelAlgorithm.py" />
|
||||
<Content Include="DynamicSecurityDataAlgorithm.py" />
|
||||
<Content Include="ConfidenceWeightedFrameworkAlgorithm.py" />
|
||||
<Content Include="ExtendedMarketTradingRegressionAlgorithm.py" />
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
<Compile Include="ConstituentsQC500GeneratorAlgorithm.py" />
|
||||
<Compile Include="ConstituentsUniverseRegressionAlgorithm.py" />
|
||||
<Compile Include="ConvertToFrameworkAlgorithm.py" />
|
||||
<Compile Include="CustomBuyingPowerModelAlgorithm.py" />
|
||||
<Compile Include="CustomDataAddDataCoarseSelectionRegressionAlgorithm.py" />
|
||||
<Compile Include="CustomDataAddDataOnSecuritiesChangedRegressionAlgorithm.py" />
|
||||
<Compile Include="CustomDataAddDataRegressionAlgorithm.py" />
|
||||
|
||||
@@ -69,9 +69,20 @@ namespace QuantConnect.Algorithm
|
||||
/// </summary>
|
||||
public void FrameworkPostInitialize()
|
||||
{
|
||||
//Prevents execution in the case of cash brokerage with IExecutionModel and IPortfolioConstructionModel
|
||||
if (PortfolioConstruction.GetType() != typeof(NullPortfolioConstructionModel)
|
||||
&& Execution.GetType() != typeof(NullExecutionModel)
|
||||
&& BrokerageModel.AccountType == AccountType.Cash)
|
||||
{
|
||||
throw new InvalidOperationException($"Non null {nameof(IExecutionModel)} and {nameof(IPortfolioConstructionModel)} are currently unsuitable for Cash Modeled brokerages (e.g. GDAX) and may result in unexpected trades."
|
||||
+ " To prevent possible user error we've restricted them to Margin trading. You can select margin account types with"
|
||||
+ $" SetBrokerage( ... AccountType.Margin). Or please set them to {nameof(NullExecutionModel)}, {nameof(NullPortfolioConstructionModel)}");
|
||||
}
|
||||
foreach (var universe in UniverseSelection.CreateUniverses(this))
|
||||
{
|
||||
AddUniverse(universe);
|
||||
// on purpose we don't call 'AddUniverse' here so that these universes don't get registered as user added
|
||||
// this is so that later during 'UniverseSelection.CreateUniverses' we wont remove them from UniverseManager
|
||||
_pendingUniverseAdditions.Add(universe);
|
||||
}
|
||||
|
||||
if (DebugMode)
|
||||
@@ -94,8 +105,7 @@ namespace QuantConnect.Algorithm
|
||||
foreach (var ukvp in UniverseManager)
|
||||
{
|
||||
var universeSymbol = ukvp.Key;
|
||||
var qcUserDefined = UserDefinedUniverse.CreateSymbol(ukvp.Value.SecurityType, ukvp.Value.Market);
|
||||
if (universeSymbol.Equals(qcUserDefined))
|
||||
if (_userAddedUniverses.Contains(universeSymbol))
|
||||
{
|
||||
// prevent removal of qc algorithm created user defined universes
|
||||
continue;
|
||||
@@ -216,16 +226,7 @@ namespace QuantConnect.Algorithm
|
||||
Log($"{Time}: RISK ADJUSTED TARGETS: {string.Join(" | ", riskAdjustedTargets.Select(t => t.ToString()).OrderBy(t => t))}");
|
||||
}
|
||||
}
|
||||
|
||||
if (riskAdjustedTargets.Length > 0
|
||||
&& Execution.GetType() != typeof(NullExecutionModel)
|
||||
&& BrokerageModel.AccountType == AccountType.Cash)
|
||||
{
|
||||
throw new InvalidOperationException($"Non null {nameof(IExecutionModel)} and {nameof(IPortfolioConstructionModel)} are currently unsuitable for Cash Modeled brokerages (e.g. GDAX) and may result in unexpected trades."
|
||||
+ " To prevent possible user error we've restricted them to Margin trading. You can select margin account types with"
|
||||
+ $" SetBrokerage( ... AccountType.Margin). Or please set them to {nameof(NullExecutionModel)}, {nameof(NullPortfolioConstructionModel)}");
|
||||
}
|
||||
|
||||
|
||||
Execution.Execute(this, riskAdjustedTargets);
|
||||
}
|
||||
|
||||
|
||||
@@ -696,18 +696,21 @@ namespace QuantConnect.Algorithm
|
||||
|
||||
private SecurityExchangeHours GetExchangeHours(Symbol symbol)
|
||||
{
|
||||
Security security;
|
||||
if (Securities.TryGetValue(symbol, out security))
|
||||
{
|
||||
return security.Exchange.Hours;
|
||||
}
|
||||
|
||||
return GetMarketHours(symbol).ExchangeHours;
|
||||
}
|
||||
|
||||
private MarketHoursDatabase.Entry GetMarketHours(Symbol symbol)
|
||||
{
|
||||
return MarketHoursDatabase.GetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType);
|
||||
var hoursEntry = MarketHoursDatabase.GetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType);
|
||||
|
||||
// user can override the exchange hours in algorithm, i.e. HistoryAlgorithm
|
||||
Security security;
|
||||
if (Securities.TryGetValue(symbol, out security))
|
||||
{
|
||||
return new MarketHoursDatabase.Entry(hoursEntry.DataTimeZone, security.Exchange.Hours);
|
||||
}
|
||||
|
||||
return hoursEntry;
|
||||
}
|
||||
|
||||
private Resolution GetResolution(Symbol symbol, Resolution? resolution)
|
||||
|
||||
@@ -227,7 +227,7 @@ namespace QuantConnect.Algorithm
|
||||
{
|
||||
foreach (var indicator in indicators)
|
||||
{
|
||||
Plot(chart, indicator.Name, indicator);
|
||||
Plot(chart, indicator.Name, indicator.Current.Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -230,22 +230,22 @@ namespace QuantConnect.Algorithm
|
||||
/// will be executed on day changes in the NewYork time zone (<see cref="TimeZones.NewYork"/>
|
||||
/// </summary>
|
||||
/// <param name="pyObject">Defines an initial coarse selection</param>
|
||||
public void AddUniverse(PyObject pyObject)
|
||||
public Universe AddUniverse(PyObject pyObject)
|
||||
{
|
||||
Func<IEnumerable<CoarseFundamental>, object> coarseFunc;
|
||||
Universe universe;
|
||||
|
||||
if (pyObject.TryConvert(out universe))
|
||||
{
|
||||
AddUniverse(universe);
|
||||
return AddUniverse(universe);
|
||||
}
|
||||
else if (pyObject.TryConvert(out universe, allowPythonDerivative: true))
|
||||
{
|
||||
AddUniverse(new UniversePythonWrapper(pyObject));
|
||||
return AddUniverse(new UniversePythonWrapper(pyObject));
|
||||
}
|
||||
else if (pyObject.TryConvertToDelegate(out coarseFunc))
|
||||
{
|
||||
AddUniverse(coarseFunc.ConvertToUniverseSelectionSymbolDelegate());
|
||||
return AddUniverse(coarseFunc.ConvertToUniverseSelectionSymbolDelegate());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -262,7 +262,7 @@ namespace QuantConnect.Algorithm
|
||||
/// </summary>
|
||||
/// <param name="pyObject">Defines an initial coarse selection or a universe</param>
|
||||
/// <param name="pyfine">Defines a more detailed selection with access to more data</param>
|
||||
public void AddUniverse(PyObject pyObject, PyObject pyfine)
|
||||
public Universe AddUniverse(PyObject pyObject, PyObject pyfine)
|
||||
{
|
||||
Func<IEnumerable<CoarseFundamental>, object> coarseFunc;
|
||||
Func<IEnumerable<FineFundamental>, object> fineFunc;
|
||||
@@ -270,11 +270,11 @@ namespace QuantConnect.Algorithm
|
||||
|
||||
if (pyObject.TryConvert(out universe) && pyfine.TryConvertToDelegate(out fineFunc))
|
||||
{
|
||||
AddUniverse(universe, fineFunc.ConvertToUniverseSelectionSymbolDelegate());
|
||||
return AddUniverse(universe, fineFunc.ConvertToUniverseSelectionSymbolDelegate());
|
||||
}
|
||||
else if (pyObject.TryConvertToDelegate(out coarseFunc) && pyfine.TryConvertToDelegate(out fineFunc))
|
||||
{
|
||||
AddUniverse(coarseFunc.ConvertToUniverseSelectionSymbolDelegate(),
|
||||
return AddUniverse(coarseFunc.ConvertToUniverseSelectionSymbolDelegate(),
|
||||
fineFunc.ConvertToUniverseSelectionSymbolDelegate());
|
||||
}
|
||||
else
|
||||
@@ -293,10 +293,10 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="name">A unique name for this universe</param>
|
||||
/// <param name="resolution">The resolution this universe should be triggered on</param>
|
||||
/// <param name="pySelector">Function delegate that accepts a DateTime and returns a collection of string symbols</param>
|
||||
public void AddUniverse(string name, Resolution resolution, PyObject pySelector)
|
||||
public Universe AddUniverse(string name, Resolution resolution, PyObject pySelector)
|
||||
{
|
||||
var selector = pySelector.ConvertToDelegate<Func<DateTime, object>>();
|
||||
AddUniverse(name, resolution, selector.ConvertToUniverseSelectionStringDelegate());
|
||||
return AddUniverse(name, resolution, selector.ConvertToUniverseSelectionStringDelegate());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -305,10 +305,10 @@ namespace QuantConnect.Algorithm
|
||||
/// </summary>
|
||||
/// <param name="name">A unique name for this universe</param>
|
||||
/// <param name="pySelector">Function delegate that accepts a DateTime and returns a collection of string symbols</param>
|
||||
public void AddUniverse(string name, PyObject pySelector)
|
||||
public Universe AddUniverse(string name, PyObject pySelector)
|
||||
{
|
||||
var selector = pySelector.ConvertToDelegate<Func<DateTime, object>>();
|
||||
AddUniverse(name, selector.ConvertToUniverseSelectionStringDelegate());
|
||||
return AddUniverse(name, selector.ConvertToUniverseSelectionStringDelegate());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -320,10 +320,10 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="market">The market of the universe</param>
|
||||
/// <param name="universeSettings">The subscription settings used for securities added from this universe</param>
|
||||
/// <param name="pySelector">Function delegate that accepts a DateTime and returns a collection of string symbols</param>
|
||||
public void AddUniverse(SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, PyObject pySelector)
|
||||
public Universe AddUniverse(SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, PyObject pySelector)
|
||||
{
|
||||
var selector = pySelector.ConvertToDelegate<Func<DateTime, object>>();
|
||||
AddUniverse(securityType, name, resolution, market, universeSettings, selector.ConvertToUniverseSelectionStringDelegate());
|
||||
return AddUniverse(securityType, name, resolution, market, universeSettings, selector.ConvertToUniverseSelectionStringDelegate());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -334,9 +334,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="T">The data type</param>
|
||||
/// <param name="name">A unique name for this universe</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse(PyObject T, string name, PyObject selector)
|
||||
public Universe AddUniverse(PyObject T, string name, PyObject selector)
|
||||
{
|
||||
AddUniverse(T.CreateType(), SecurityType.Equity, name, Resolution.Daily, Market.USA, UniverseSettings, selector);
|
||||
return AddUniverse(T.CreateType(), SecurityType.Equity, name, Resolution.Daily, Market.USA, UniverseSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -348,9 +348,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="name">A unique name for this universe</param>
|
||||
/// <param name="resolution">The epected resolution of the universe data</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse(PyObject T, string name, Resolution resolution, PyObject selector)
|
||||
public Universe AddUniverse(PyObject T, string name, Resolution resolution, PyObject selector)
|
||||
{
|
||||
AddUniverse(T.CreateType(), SecurityType.Equity, name, resolution, Market.USA, UniverseSettings, selector);
|
||||
return AddUniverse(T.CreateType(), SecurityType.Equity, name, resolution, Market.USA, UniverseSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -363,9 +363,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="resolution">The epected resolution of the universe data</param>
|
||||
/// <param name="universeSettings">The settings used for securities added by this universe</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse(PyObject T, string name, Resolution resolution, UniverseSettings universeSettings, PyObject selector)
|
||||
public Universe AddUniverse(PyObject T, string name, Resolution resolution, UniverseSettings universeSettings, PyObject selector)
|
||||
{
|
||||
AddUniverse(T.CreateType(), SecurityType.Equity, name, resolution, Market.USA, universeSettings, selector);
|
||||
return AddUniverse(T.CreateType(), SecurityType.Equity, name, resolution, Market.USA, universeSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -377,9 +377,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="name">A unique name for this universe</param>
|
||||
/// <param name="universeSettings">The settings used for securities added by this universe</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse(PyObject T, string name, UniverseSettings universeSettings, PyObject selector)
|
||||
public Universe AddUniverse(PyObject T, string name, UniverseSettings universeSettings, PyObject selector)
|
||||
{
|
||||
AddUniverse(T.CreateType(), SecurityType.Equity, name, Resolution.Daily, Market.USA, universeSettings, selector);
|
||||
return AddUniverse(T.CreateType(), SecurityType.Equity, name, Resolution.Daily, Market.USA, universeSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -392,9 +392,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="resolution">The epected resolution of the universe data</param>
|
||||
/// <param name="market">The market for selected symbols</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse(PyObject T, SecurityType securityType, string name, Resolution resolution, string market, PyObject selector)
|
||||
public Universe AddUniverse(PyObject T, SecurityType securityType, string name, Resolution resolution, string market, PyObject selector)
|
||||
{
|
||||
AddUniverse(T.CreateType(), securityType, name, resolution, market, UniverseSettings, selector);
|
||||
return AddUniverse(T.CreateType(), securityType, name, resolution, market, UniverseSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -407,9 +407,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="market">The market for selected symbols</param>
|
||||
/// <param name="universeSettings">The subscription settings to use for newly created subscriptions</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse(PyObject T, SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, PyObject selector)
|
||||
public Universe AddUniverse(PyObject T, SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, PyObject selector)
|
||||
{
|
||||
AddUniverse(T.CreateType(), securityType, name, resolution, market, universeSettings, selector);
|
||||
return AddUniverse(T.CreateType(), securityType, name, resolution, market, universeSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -422,7 +422,7 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="market">The market for selected symbols</param>
|
||||
/// <param name="universeSettings">The subscription settings to use for newly created subscriptions</param>
|
||||
/// <param name="pySelector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse(Type dataType, SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, PyObject pySelector)
|
||||
public Universe AddUniverse(Type dataType, SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, PyObject pySelector)
|
||||
{
|
||||
var marketHoursDbEntry = MarketHoursDatabase.GetEntry(market, name, securityType);
|
||||
var dataTimeZone = marketHoursDbEntry.DataTimeZone;
|
||||
@@ -432,7 +432,7 @@ namespace QuantConnect.Algorithm
|
||||
|
||||
var selector = pySelector.ConvertToDelegate<Func<IEnumerable<IBaseData>, object>>();
|
||||
|
||||
AddUniverse(new FuncUniverse(config, universeSettings, SecurityInitializer, baseDatas =>
|
||||
return AddUniverse(new FuncUniverse(config, universeSettings, SecurityInitializer, baseDatas =>
|
||||
{
|
||||
var result = selector(baseDatas);
|
||||
return ReferenceEquals(result, Universe.Unchanged)
|
||||
@@ -442,6 +442,30 @@ namespace QuantConnect.Algorithm
|
||||
));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new universe selection model and adds it to the algorithm. This universe selection model will chain to the security
|
||||
/// changes of a given <see cref="Universe"/> selection output and create a new <see cref="OptionChainUniverse"/> for each of them
|
||||
/// </summary>
|
||||
/// <param name="universe">The universe we want to chain an option universe selection model too</param>
|
||||
/// <param name="optionFilter">The option filter universe to use</param>
|
||||
public void AddUniverseOptions(PyObject universe, PyObject optionFilter)
|
||||
{
|
||||
Func<OptionFilterUniverse, OptionFilterUniverse> convertedOptionChain;
|
||||
Universe universeToChain;
|
||||
|
||||
if (universe.TryConvert(out universeToChain) && optionFilter.TryConvertToDelegate(out convertedOptionChain))
|
||||
{
|
||||
AddUniverseOptions(universeToChain, convertedOptionChain);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
throw new ArgumentException($"QCAlgorithm.AddChainedEquityOptionUniverseSelectionModel: {universe.Repr()} or {optionFilter.Repr()} is not a valid argument.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
|
||||
/// from the consolidator.
|
||||
@@ -780,7 +804,7 @@ namespace QuantConnect.Algorithm
|
||||
var res = GetResolution(x, resolution);
|
||||
var exchange = GetExchangeHours(x);
|
||||
var start = _historyRequestFactory.GetStartTimeAlgoTz(x, periods, res, exchange, config.DataTimeZone);
|
||||
return _historyRequestFactory.CreateHistoryRequest(config, start, Time.RoundDown(res.ToTimeSpan()), exchange, res);
|
||||
return _historyRequestFactory.CreateHistoryRequest(config, start, Time, exchange, res);
|
||||
});
|
||||
|
||||
return PandasConverter.GetDataFrame(History(requests.Where(x => x != null)).Memoize());
|
||||
@@ -843,8 +867,7 @@ namespace QuantConnect.Algorithm
|
||||
var res = GetResolution(symbol, resolution);
|
||||
var marketHours = GetMarketHours(symbol);
|
||||
var start = _historyRequestFactory.GetStartTimeAlgoTz(symbol, periods, res, marketHours.ExchangeHours, marketHours.DataTimeZone);
|
||||
var end = Time.RoundDown(res.ToTimeSpan());
|
||||
return History(type, symbol, start, end, resolution);
|
||||
return History(type, symbol, start, Time, resolution);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -488,9 +488,11 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="tag">String tag for the order (optional)</param>
|
||||
public OrderTicket ExerciseOption(Symbol optionSymbol, int quantity, bool asynchronous = false, string tag = "")
|
||||
{
|
||||
var option = (Option)Securities[optionSymbol];
|
||||
var option = (Option) Securities[optionSymbol];
|
||||
|
||||
var request = CreateSubmitOrderRequest(OrderType.OptionExercise, option, quantity, tag, DefaultOrderProperties?.Clone());
|
||||
// SubmitOrderRequest.Quantity indicates the change in holdings quantity, therefore manual exercise quantities must be negative
|
||||
// PreOrderChecksImpl confirms that we don't hold a short position, so we're lenient here and accept +/- quantity values
|
||||
var request = CreateSubmitOrderRequest(OrderType.OptionExercise, option, -Math.Abs(quantity), tag, DefaultOrderProperties?.Clone());
|
||||
|
||||
// If warming up, do not submit
|
||||
if (IsWarmingUp)
|
||||
@@ -657,7 +659,9 @@ namespace QuantConnect.Algorithm
|
||||
Security security;
|
||||
if (!Securities.TryGetValue(request.Symbol, out security))
|
||||
{
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.MissingSecurity, "You haven't requested " + request.Symbol.ToString() + " data. Add this with AddSecurity() in the Initialize() Method.");
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.MissingSecurity,
|
||||
$"You haven't requested {request.Symbol} data. Add this with AddSecurity() in the Initialize() Method."
|
||||
);
|
||||
}
|
||||
|
||||
//Ordering 0 is useless.
|
||||
@@ -677,7 +681,9 @@ namespace QuantConnect.Algorithm
|
||||
|
||||
if (!security.IsTradable)
|
||||
{
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.NonTradableSecurity, "The security with symbol '" + request.Symbol.ToString() + "' is marked as non-tradable.");
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.NonTradableSecurity,
|
||||
$"The security with symbol '{request.Symbol}' is marked as non-tradable."
|
||||
);
|
||||
}
|
||||
|
||||
var price = security.Price;
|
||||
@@ -685,13 +691,17 @@ namespace QuantConnect.Algorithm
|
||||
//Check the exchange is open before sending a market on close orders
|
||||
if (request.OrderType == OrderType.MarketOnClose && !security.Exchange.ExchangeOpen)
|
||||
{
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.ExchangeNotOpen, request.OrderType + " order and exchange not open.");
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.ExchangeNotOpen,
|
||||
$"{request.OrderType} order and exchange not open."
|
||||
);
|
||||
}
|
||||
|
||||
//Check the exchange is open before sending a exercise orders
|
||||
if (request.OrderType == OrderType.OptionExercise && !security.Exchange.ExchangeOpen)
|
||||
{
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.ExchangeNotOpen, request.OrderType + " order and exchange not open.");
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.ExchangeNotOpen,
|
||||
$"{request.OrderType} order and exchange not open."
|
||||
);
|
||||
}
|
||||
|
||||
if (price == 0)
|
||||
@@ -704,11 +714,15 @@ namespace QuantConnect.Algorithm
|
||||
var quoteCurrency = security.QuoteCurrency.Symbol;
|
||||
if (!Portfolio.CashBook.TryGetValue(quoteCurrency, out quoteCash))
|
||||
{
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.QuoteCurrencyRequired, request.Symbol.Value + ": requires " + quoteCurrency + " in the cashbook to trade.");
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.QuoteCurrencyRequired,
|
||||
$"{request.Symbol.Value}: requires {quoteCurrency} in the cashbook to trade."
|
||||
);
|
||||
}
|
||||
if (security.QuoteCurrency.ConversionRate == 0m)
|
||||
{
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.ConversionRateZero, request.Symbol.Value + ": requires " + quoteCurrency + " to have a non-zero conversion rate. This can be caused by lack of data.");
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.ConversionRateZero,
|
||||
$"{request.Symbol.Value}: requires {quoteCurrency} to have a non-zero conversion rate. This can be caused by lack of data."
|
||||
);
|
||||
}
|
||||
|
||||
// need to also check base currency existence/conversion rate on forex orders
|
||||
@@ -718,18 +732,24 @@ namespace QuantConnect.Algorithm
|
||||
var baseCurrency = ((IBaseCurrencySymbol)security).BaseCurrencySymbol;
|
||||
if (!Portfolio.CashBook.TryGetValue(baseCurrency, out baseCash))
|
||||
{
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.ForexBaseAndQuoteCurrenciesRequired, request.Symbol.Value + ": requires " + baseCurrency + " and " + quoteCurrency + " in the cashbook to trade.");
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.ForexBaseAndQuoteCurrenciesRequired,
|
||||
$"{request.Symbol.Value}: requires {baseCurrency} and {quoteCurrency} in the cashbook to trade."
|
||||
);
|
||||
}
|
||||
if (baseCash.ConversionRate == 0m)
|
||||
{
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.ForexConversionRateZero, request.Symbol.Value + ": requires " + baseCurrency + " and " + quoteCurrency + " to have non-zero conversion rates. This can be caused by lack of data.");
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.ForexConversionRateZero,
|
||||
$"{request.Symbol.Value}: requires {baseCurrency} and {quoteCurrency} to have non-zero conversion rates. This can be caused by lack of data."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//Make sure the security has some data:
|
||||
if (!security.HasData)
|
||||
{
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.SecurityHasNoData, "There is no data for this symbol yet, please check the security.HasData flag to ensure there is at least one data point.");
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.SecurityHasNoData,
|
||||
"There is no data for this symbol yet, please check the security.HasData flag to ensure there is at least one data point."
|
||||
);
|
||||
}
|
||||
|
||||
// We've already processed too many orders: max 10k
|
||||
@@ -737,28 +757,38 @@ namespace QuantConnect.Algorithm
|
||||
{
|
||||
Status = AlgorithmStatus.Stopped;
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.ExceededMaximumOrders,
|
||||
$"You have exceeded maximum number of orders ({_maxOrders.ToStringInvariant()}), for unlimited orders upgrade your account."
|
||||
Invariant($"You have exceeded maximum number of orders ({_maxOrders}), for unlimited orders upgrade your account.")
|
||||
);
|
||||
}
|
||||
|
||||
if (request.OrderType == OrderType.OptionExercise)
|
||||
{
|
||||
if (security.Type != SecurityType.Option)
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.NonExercisableSecurity, "The security with symbol '" + request.Symbol.ToString() + "' is not exercisable.");
|
||||
{
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.NonExercisableSecurity,
|
||||
$"The security with symbol '{request.Symbol}' is not exercisable."
|
||||
);
|
||||
}
|
||||
|
||||
if (security.Holdings.IsShort)
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.UnsupportedRequestType, "The security with symbol '" + request.Symbol.ToString() + "' has a short option position. Only long option positions are exercisable.");
|
||||
{
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.UnsupportedRequestType,
|
||||
$"The security with symbol '{request.Symbol}' has a short option position. Only long option positions are exercisable."
|
||||
);
|
||||
}
|
||||
|
||||
if (request.Quantity > security.Holdings.Quantity)
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.UnsupportedRequestType, "Cannot exercise more contracts of '" + request.Symbol.ToString() + "' than is currently available in the portfolio. ");
|
||||
|
||||
if (request.Quantity <= 0.0m)
|
||||
OrderResponse.ZeroQuantity(request);
|
||||
if (Math.Abs(request.Quantity) > security.Holdings.Quantity)
|
||||
{
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.UnsupportedRequestType,
|
||||
$"Cannot exercise more contracts of '{request.Symbol}' than is currently available in the portfolio. "
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (request.OrderType == OrderType.MarketOnClose)
|
||||
{
|
||||
var nextMarketClose = security.Exchange.Hours.GetNextMarketClose(security.LocalTime, false);
|
||||
|
||||
// must be submitted with at least 10 minutes in trading day, add buffer allow order submission
|
||||
var latestSubmissionTime = nextMarketClose.Subtract(Orders.MarketOnCloseOrder.DefaultSubmissionTimeBuffer);
|
||||
if (!security.Exchange.ExchangeOpen || Time > latestSubmissionTime)
|
||||
@@ -766,7 +796,9 @@ namespace QuantConnect.Algorithm
|
||||
// tell the user we require a 16 minute buffer, on minute data in live a user will receive the 3:44->3:45 bar at 3:45,
|
||||
// this is already too late to submit one of these orders, so make the user do it at the 3:43->3:44 bar so it's submitted
|
||||
// to the brokerage before 3:45.
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.MarketOnCloseOrderTooLate, "MarketOnClose orders must be placed with at least a 16 minute buffer before market close.");
|
||||
return OrderResponse.Error(request, OrderResponseErrorCode.MarketOnCloseOrderTooLate,
|
||||
"MarketOnClose orders must be placed with at least a 16 minute buffer before market close."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QuantConnect.Algorithm.Selection;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Fundamental;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
@@ -32,6 +33,8 @@ namespace QuantConnect.Algorithm
|
||||
private readonly object _pendingUniverseAdditionsLock = new object();
|
||||
private readonly List<UserDefinedUniverseAddition> _pendingUserDefinedUniverseSecurityAdditions = new List<UserDefinedUniverseAddition>();
|
||||
private readonly List<Universe> _pendingUniverseAdditions = new List<Universe>();
|
||||
// this is so that later during 'UniverseSelection.CreateUniverses' we wont remove these user universes from the UniverseManager
|
||||
private readonly HashSet<Symbol> _userAddedUniverses = new HashSet<Symbol>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets universe manager which holds universes keyed by their symbol
|
||||
@@ -179,11 +182,13 @@ namespace QuantConnect.Algorithm
|
||||
/// Adds the universe to the algorithm
|
||||
/// </summary>
|
||||
/// <param name="universe">The universe to be added</param>
|
||||
public void AddUniverse(Universe universe)
|
||||
public Universe AddUniverse(Universe universe)
|
||||
{
|
||||
// The universe will be added at the end of time step, same as the AddData user defined universes.
|
||||
// This is required to be independent of the start and end date set during initialize
|
||||
_pendingUniverseAdditions.Add(universe);
|
||||
_userAddedUniverses.Add(universe.Configuration.Symbol);
|
||||
return universe;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -194,9 +199,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <typeparam name="T">The data type</typeparam>
|
||||
/// <param name="name">A unique name for this universe</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse<T>(string name, Func<IEnumerable<T>, IEnumerable<Symbol>> selector)
|
||||
public Universe AddUniverse<T>(string name, Func<IEnumerable<T>, IEnumerable<Symbol>> selector)
|
||||
{
|
||||
AddUniverse(SecurityType.Equity, name, Resolution.Daily, Market.USA, UniverseSettings, selector);
|
||||
return AddUniverse(SecurityType.Equity, name, Resolution.Daily, Market.USA, UniverseSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -207,9 +212,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <typeparam name="T">The data type</typeparam>
|
||||
/// <param name="name">A unique name for this universe</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse<T>(string name, Func<IEnumerable<T>, IEnumerable<string>> selector)
|
||||
public Universe AddUniverse<T>(string name, Func<IEnumerable<T>, IEnumerable<string>> selector)
|
||||
{
|
||||
AddUniverse(SecurityType.Equity, name, Resolution.Daily, Market.USA, UniverseSettings, selector);
|
||||
return AddUniverse(SecurityType.Equity, name, Resolution.Daily, Market.USA, UniverseSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -221,9 +226,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="name">A unique name for this universe</param>
|
||||
/// <param name="universeSettings">The settings used for securities added by this universe</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse<T>(string name, UniverseSettings universeSettings, Func<IEnumerable<T>, IEnumerable<Symbol>> selector)
|
||||
public Universe AddUniverse<T>(string name, UniverseSettings universeSettings, Func<IEnumerable<T>, IEnumerable<Symbol>> selector)
|
||||
{
|
||||
AddUniverse(SecurityType.Equity, name, Resolution.Daily, Market.USA, universeSettings, selector);
|
||||
return AddUniverse(SecurityType.Equity, name, Resolution.Daily, Market.USA, universeSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -235,9 +240,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="name">A unique name for this universe</param>
|
||||
/// <param name="universeSettings">The settings used for securities added by this universe</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse<T>(string name, UniverseSettings universeSettings, Func<IEnumerable<T>, IEnumerable<string>> selector)
|
||||
public Universe AddUniverse<T>(string name, UniverseSettings universeSettings, Func<IEnumerable<T>, IEnumerable<string>> selector)
|
||||
{
|
||||
AddUniverse(SecurityType.Equity, name, Resolution.Daily, Market.USA, universeSettings, selector);
|
||||
return AddUniverse(SecurityType.Equity, name, Resolution.Daily, Market.USA, universeSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -249,9 +254,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="name">A unique name for this universe</param>
|
||||
/// <param name="resolution">The epected resolution of the universe data</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse<T>(string name, Resolution resolution, Func<IEnumerable<T>, IEnumerable<Symbol>> selector)
|
||||
public Universe AddUniverse<T>(string name, Resolution resolution, Func<IEnumerable<T>, IEnumerable<Symbol>> selector)
|
||||
{
|
||||
AddUniverse(SecurityType.Equity, name, resolution, Market.USA, UniverseSettings, selector);
|
||||
return AddUniverse(SecurityType.Equity, name, resolution, Market.USA, UniverseSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -263,9 +268,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="name">A unique name for this universe</param>
|
||||
/// <param name="resolution">The epected resolution of the universe data</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse<T>(string name, Resolution resolution, Func<IEnumerable<T>, IEnumerable<string>> selector)
|
||||
public Universe AddUniverse<T>(string name, Resolution resolution, Func<IEnumerable<T>, IEnumerable<string>> selector)
|
||||
{
|
||||
AddUniverse(SecurityType.Equity, name, resolution, Market.USA, UniverseSettings, selector);
|
||||
return AddUniverse(SecurityType.Equity, name, resolution, Market.USA, UniverseSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -278,9 +283,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="resolution">The epected resolution of the universe data</param>
|
||||
/// <param name="universeSettings">The settings used for securities added by this universe</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse<T>(string name, Resolution resolution, UniverseSettings universeSettings, Func<IEnumerable<T>, IEnumerable<Symbol>> selector)
|
||||
public Universe AddUniverse<T>(string name, Resolution resolution, UniverseSettings universeSettings, Func<IEnumerable<T>, IEnumerable<Symbol>> selector)
|
||||
{
|
||||
AddUniverse(SecurityType.Equity, name, resolution, Market.USA, universeSettings, selector);
|
||||
return AddUniverse(SecurityType.Equity, name, resolution, Market.USA, universeSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -293,9 +298,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="resolution">The epected resolution of the universe data</param>
|
||||
/// <param name="universeSettings">The settings used for securities added by this universe</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse<T>(string name, Resolution resolution, UniverseSettings universeSettings, Func<IEnumerable<T>, IEnumerable<string>> selector)
|
||||
public Universe AddUniverse<T>(string name, Resolution resolution, UniverseSettings universeSettings, Func<IEnumerable<T>, IEnumerable<string>> selector)
|
||||
{
|
||||
AddUniverse(SecurityType.Equity, name, resolution, Market.USA, universeSettings, selector);
|
||||
return AddUniverse(SecurityType.Equity, name, resolution, Market.USA, universeSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -308,9 +313,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="resolution">The epected resolution of the universe data</param>
|
||||
/// <param name="market">The market for selected symbols</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse<T>(SecurityType securityType, string name, Resolution resolution, string market, Func<IEnumerable<T>, IEnumerable<Symbol>> selector)
|
||||
public Universe AddUniverse<T>(SecurityType securityType, string name, Resolution resolution, string market, Func<IEnumerable<T>, IEnumerable<Symbol>> selector)
|
||||
{
|
||||
AddUniverse(securityType, name, resolution, market, UniverseSettings, selector);
|
||||
return AddUniverse(securityType, name, resolution, market, UniverseSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -323,9 +328,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="resolution">The epected resolution of the universe data</param>
|
||||
/// <param name="market">The market for selected symbols</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse<T>(SecurityType securityType, string name, Resolution resolution, string market, Func<IEnumerable<T>, IEnumerable<string>> selector)
|
||||
public Universe AddUniverse<T>(SecurityType securityType, string name, Resolution resolution, string market, Func<IEnumerable<T>, IEnumerable<string>> selector)
|
||||
{
|
||||
AddUniverse(securityType, name, resolution, market, UniverseSettings, selector);
|
||||
return AddUniverse(securityType, name, resolution, market, UniverseSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -338,14 +343,14 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="market">The market for selected symbols</param>
|
||||
/// <param name="universeSettings">The subscription settings to use for newly created subscriptions</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse<T>(SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, Func<IEnumerable<T>, IEnumerable<Symbol>> selector)
|
||||
public Universe AddUniverse<T>(SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, Func<IEnumerable<T>, IEnumerable<Symbol>> selector)
|
||||
{
|
||||
var marketHoursDbEntry = MarketHoursDatabase.GetEntry(market, name, securityType);
|
||||
var dataTimeZone = marketHoursDbEntry.DataTimeZone;
|
||||
var exchangeTimeZone = marketHoursDbEntry.ExchangeHours.TimeZone;
|
||||
var symbol = QuantConnect.Symbol.Create(name, securityType, market, baseDataType: typeof(T));
|
||||
var config = new SubscriptionDataConfig(typeof(T), symbol, resolution, dataTimeZone, exchangeTimeZone, false, false, true, true, isFilteredSubscription: false);
|
||||
AddUniverse(new FuncUniverse(config, universeSettings, SecurityInitializer, d => selector(d.OfType<T>())));
|
||||
return AddUniverse(new FuncUniverse(config, universeSettings, SecurityInitializer, d => selector(d.OfType<T>())));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -358,14 +363,14 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="market">The market for selected symbols</param>
|
||||
/// <param name="universeSettings">The subscription settings to use for newly created subscriptions</param>
|
||||
/// <param name="selector">Function delegate that performs selection on the universe data</param>
|
||||
public void AddUniverse<T>(SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, Func<IEnumerable<T>, IEnumerable<string>> selector)
|
||||
public Universe AddUniverse<T>(SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, Func<IEnumerable<T>, IEnumerable<string>> selector)
|
||||
{
|
||||
var marketHoursDbEntry = MarketHoursDatabase.GetEntry(market, name, securityType);
|
||||
var dataTimeZone = marketHoursDbEntry.DataTimeZone;
|
||||
var exchangeTimeZone = marketHoursDbEntry.ExchangeHours.TimeZone;
|
||||
var symbol = QuantConnect.Symbol.Create(name, securityType, market, baseDataType: typeof(T));
|
||||
var config = new SubscriptionDataConfig(typeof(T), symbol, resolution, dataTimeZone, exchangeTimeZone, false, false, true, true, isFilteredSubscription: false);
|
||||
AddUniverse(new FuncUniverse(config, universeSettings, SecurityInitializer,
|
||||
return AddUniverse(new FuncUniverse(config, universeSettings, SecurityInitializer,
|
||||
d => selector(d.OfType<T>()).Select(x => QuantConnect.Symbol.Create(x, securityType, market, baseDataType: typeof(T))))
|
||||
);
|
||||
}
|
||||
@@ -375,9 +380,9 @@ namespace QuantConnect.Algorithm
|
||||
/// will be executed on day changes in the NewYork time zone (<see cref="TimeZones.NewYork"/>
|
||||
/// </summary>
|
||||
/// <param name="selector">Defines an initial coarse selection</param>
|
||||
public void AddUniverse(Func<IEnumerable<CoarseFundamental>, IEnumerable<Symbol>> selector)
|
||||
public Universe AddUniverse(Func<IEnumerable<CoarseFundamental>, IEnumerable<Symbol>> selector)
|
||||
{
|
||||
AddUniverse(new CoarseFundamentalUniverse(UniverseSettings, SecurityInitializer, selector));
|
||||
return AddUniverse(new CoarseFundamentalUniverse(UniverseSettings, SecurityInitializer, selector));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -386,11 +391,11 @@ namespace QuantConnect.Algorithm
|
||||
/// </summary>
|
||||
/// <param name="coarseSelector">Defines an initial coarse selection</param>
|
||||
/// <param name="fineSelector">Defines a more detailed selection with access to more data</param>
|
||||
public void AddUniverse(Func<IEnumerable<CoarseFundamental>, IEnumerable<Symbol>> coarseSelector, Func<IEnumerable<FineFundamental>, IEnumerable<Symbol>> fineSelector)
|
||||
public Universe AddUniverse(Func<IEnumerable<CoarseFundamental>, IEnumerable<Symbol>> coarseSelector, Func<IEnumerable<FineFundamental>, IEnumerable<Symbol>> fineSelector)
|
||||
{
|
||||
var coarse = new CoarseFundamentalUniverse(UniverseSettings, SecurityInitializer, coarseSelector);
|
||||
|
||||
AddUniverse(new FineFundamentalFilteredUniverse(coarse, fineSelector));
|
||||
return AddUniverse(new FineFundamentalFilteredUniverse(coarse, fineSelector));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -399,9 +404,9 @@ namespace QuantConnect.Algorithm
|
||||
/// </summary>
|
||||
/// <param name="universe">The universe to be filtered with fine fundamental selection</param>
|
||||
/// <param name="fineSelector">Defines a more detailed selection with access to more data</param>
|
||||
public void AddUniverse(Universe universe, Func<IEnumerable<FineFundamental>, IEnumerable<Symbol>> fineSelector)
|
||||
public Universe AddUniverse(Universe universe, Func<IEnumerable<FineFundamental>, IEnumerable<Symbol>> fineSelector)
|
||||
{
|
||||
AddUniverse(new FineFundamentalFilteredUniverse(universe, fineSelector));
|
||||
return AddUniverse(new FineFundamentalFilteredUniverse(universe, fineSelector));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -410,9 +415,9 @@ namespace QuantConnect.Algorithm
|
||||
/// </summary>
|
||||
/// <param name="name">A unique name for this universe</param>
|
||||
/// <param name="selector">Function delegate that accepts a DateTime and returns a collection of string symbols</param>
|
||||
public void AddUniverse(string name, Func<DateTime, IEnumerable<string>> selector)
|
||||
public Universe AddUniverse(string name, Func<DateTime, IEnumerable<string>> selector)
|
||||
{
|
||||
AddUniverse(SecurityType.Equity, name, Resolution.Daily, Market.USA, UniverseSettings, selector);
|
||||
return AddUniverse(SecurityType.Equity, name, Resolution.Daily, Market.USA, UniverseSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -422,9 +427,9 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="name">A unique name for this universe</param>
|
||||
/// <param name="resolution">The resolution this universe should be triggered on</param>
|
||||
/// <param name="selector">Function delegate that accepts a DateTime and returns a collection of string symbols</param>
|
||||
public void AddUniverse(string name, Resolution resolution, Func<DateTime, IEnumerable<string>> selector)
|
||||
public Universe AddUniverse(string name, Resolution resolution, Func<DateTime, IEnumerable<string>> selector)
|
||||
{
|
||||
AddUniverse(SecurityType.Equity, name, resolution, Market.USA, UniverseSettings, selector);
|
||||
return AddUniverse(SecurityType.Equity, name, resolution, Market.USA, UniverseSettings, selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -436,14 +441,25 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="market">The market of the universe</param>
|
||||
/// <param name="universeSettings">The subscription settings used for securities added from this universe</param>
|
||||
/// <param name="selector">Function delegate that accepts a DateTime and returns a collection of string symbols</param>
|
||||
public void AddUniverse(SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, Func<DateTime, IEnumerable<string>> selector)
|
||||
public Universe AddUniverse(SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, Func<DateTime, IEnumerable<string>> selector)
|
||||
{
|
||||
var marketHoursDbEntry = MarketHoursDatabase.GetEntry(market, name, securityType);
|
||||
var dataTimeZone = marketHoursDbEntry.DataTimeZone;
|
||||
var exchangeTimeZone = marketHoursDbEntry.ExchangeHours.TimeZone;
|
||||
var symbol = QuantConnect.Symbol.Create(name, securityType, market);
|
||||
var config = new SubscriptionDataConfig(typeof(CoarseFundamental), symbol, resolution, dataTimeZone, exchangeTimeZone, false, false, true, isFilteredSubscription: false);
|
||||
AddUniverse(new UserDefinedUniverse(config, universeSettings, resolution.ToTimeSpan(), selector));
|
||||
return AddUniverse(new UserDefinedUniverse(config, universeSettings, resolution.ToTimeSpan(), selector));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new universe selection model and adds it to the algorithm. This universe selection model will chain to the security
|
||||
/// changes of a given <see cref="Universe"/> selection output and create a new <see cref="OptionChainUniverse"/> for each of them
|
||||
/// </summary>
|
||||
/// <param name="universe">The universe we want to chain an option universe selection model too</param>
|
||||
/// <param name="optionFilter">The option filter universe to use</param>
|
||||
public void AddUniverseOptions(Universe universe, Func<OptionFilterUniverse, OptionFilterUniverse> optionFilter)
|
||||
{
|
||||
AddUniverseSelection(new OptionChainedUniverseSelectionModel(universe, optionFilter));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -501,7 +517,8 @@ namespace QuantConnect.Algorithm
|
||||
TimeSpan.Zero),
|
||||
QuantConnect.Time.MaxTimeSpan,
|
||||
new List<Symbol>());
|
||||
_pendingUniverseAdditions.Add(universe);
|
||||
|
||||
AddUniverse(universe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ using QuantConnect.Algorithm.Framework.Execution;
|
||||
using QuantConnect.Algorithm.Framework.Portfolio;
|
||||
using QuantConnect.Algorithm.Framework.Risk;
|
||||
using QuantConnect.Algorithm.Framework.Selection;
|
||||
using QuantConnect.Algorithm.Selection;
|
||||
using QuantConnect.Storage;
|
||||
|
||||
namespace QuantConnect.Algorithm
|
||||
@@ -958,8 +959,19 @@ namespace QuantConnect.Algorithm
|
||||
// the time rules need to know the default time zone as well
|
||||
TimeRules.SetDefaultTimeZone(timeZone);
|
||||
|
||||
// reset the current time according to the time zone
|
||||
SetDateTime(_startDate.ConvertToUtc(TimeZone));
|
||||
// In BackTest mode we reset the Algorithm time to reflect the new timezone
|
||||
// startDate is set by the user so we expect it to be for their timezone already
|
||||
// so there is no need to update it.
|
||||
if (!LiveMode)
|
||||
{
|
||||
SetDateTime(_startDate.ConvertToUtc(TimeZone));
|
||||
}
|
||||
// In live mode we need to adjust startDate to reflect the new timezone
|
||||
// startDate is set by Lean to the default timezone (New York), so we must update it here
|
||||
else
|
||||
{
|
||||
_startDate = DateTime.UtcNow.ConvertFromUtc(TimeZone).Date;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1135,6 +1147,8 @@ namespace QuantConnect.Algorithm
|
||||
"Cannot change AccountCurrency after algorithm initialized.");
|
||||
}
|
||||
|
||||
Debug($"Changing account currency from {AccountCurrency} to {accountCurrency}...");
|
||||
|
||||
Portfolio.SetAccountCurrency(accountCurrency);
|
||||
}
|
||||
|
||||
@@ -1359,7 +1373,8 @@ namespace QuantConnect.Algorithm
|
||||
Securities.SetLiveMode(live);
|
||||
if (live)
|
||||
{
|
||||
_startDate = DateTime.Today;
|
||||
// startDate is set relative to the algorithm's timezone.
|
||||
_startDate = DateTime.UtcNow.ConvertFromUtc(TimeZone).Date;
|
||||
_endDate = QuantConnect.Time.EndOfTime;
|
||||
}
|
||||
}
|
||||
@@ -1497,7 +1512,8 @@ namespace QuantConnect.Algorithm
|
||||
{
|
||||
universe = new FuturesChainUniverse((Future)security, settings);
|
||||
}
|
||||
_pendingUniverseAdditions.Add(universe);
|
||||
|
||||
AddUniverse(universe);
|
||||
}
|
||||
return security;
|
||||
}
|
||||
@@ -1607,7 +1623,7 @@ namespace QuantConnect.Algorithm
|
||||
/// <returns>The new <see cref="Option"/> security</returns>
|
||||
public Option AddOptionContract(Symbol symbol, Resolution? resolution = null, bool fillDataForward = true, decimal leverage = Security.NullLeverage)
|
||||
{
|
||||
var configs = SubscriptionManager.SubscriptionDataConfigService.Add(symbol, resolution, fillDataForward);
|
||||
var configs = SubscriptionManager.SubscriptionDataConfigService.Add(symbol, resolution, fillDataForward, dataNormalizationMode:DataNormalizationMode.Raw);
|
||||
var option = (Option)Securities.CreateSecurity(symbol, configs, leverage);
|
||||
|
||||
// add underlying if not present
|
||||
@@ -1641,8 +1657,26 @@ namespace QuantConnect.Algorithm
|
||||
equity.RefreshDataNormalizationModeProperty();
|
||||
|
||||
option.Underlying = equity;
|
||||
Securities.Add(option);
|
||||
|
||||
AddToUserDefinedUniverse(option, configs);
|
||||
// get or create the universe
|
||||
var universeSymbol = OptionContractUniverse.CreateSymbol(symbol.ID.Market, symbol.Underlying.SecurityType);
|
||||
Universe universe;
|
||||
if (!UniverseManager.TryGetValue(universeSymbol, out universe))
|
||||
{
|
||||
universe = _pendingUniverseAdditions.FirstOrDefault(u => u.Configuration.Symbol == universeSymbol)
|
||||
?? AddUniverse(new OptionContractUniverse(new SubscriptionDataConfig(configs.First(), symbol: universeSymbol), UniverseSettings));
|
||||
}
|
||||
|
||||
// update the universe
|
||||
var optionUniverse = universe as OptionContractUniverse;
|
||||
if (optionUniverse != null)
|
||||
{
|
||||
foreach (var subscriptionDataConfig in configs.Concat(underlyingConfigs))
|
||||
{
|
||||
optionUniverse.Add(subscriptionDataConfig);
|
||||
}
|
||||
}
|
||||
|
||||
return option;
|
||||
}
|
||||
@@ -1689,6 +1723,17 @@ namespace QuantConnect.Algorithm
|
||||
return AddSecurity<Crypto>(SecurityType.Crypto, ticker, resolution, market, fillDataForward, leverage, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the security with the specified symbol. This will cancel all
|
||||
/// open orders and then liquidate any existing holdings
|
||||
/// </summary>
|
||||
/// <param name="symbol">The symbol of the security to be removed</param>
|
||||
/// <remarks>Sugar syntax for <see cref="AddOptionContract"/></remarks>
|
||||
public bool RemoveOptionContract(Symbol symbol)
|
||||
{
|
||||
return RemoveSecurity(symbol);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the security with the specified symbol. This will cancel all
|
||||
/// open orders and then liquidate any existing holdings
|
||||
@@ -1744,6 +1789,7 @@ namespace QuantConnect.Algorithm
|
||||
|
||||
// finally, dispose and remove the canonical security from the universe manager
|
||||
UniverseManager.Remove(symbol);
|
||||
_userAddedUniverses.Remove(symbol);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -179,6 +179,8 @@
|
||||
<Compile Include="Selection\ManualUniverse.cs" />
|
||||
<Compile Include="Selection\ManualUniverseSelectionModel.cs" />
|
||||
<Compile Include="Selection\NullUniverseSelectionModel.cs" />
|
||||
<Compile Include="Selection\OptionChainedUniverseSelectionModel.cs" />
|
||||
<Compile Include="Selection\OptionContractUniverse.cs" />
|
||||
<Compile Include="Selection\UniverseSelectionModel.cs" />
|
||||
<Compile Include="Selection\UniverseSelectionModelPythonWrapper.cs" />
|
||||
<Compile Include="UniverseDefinitions.cs" />
|
||||
|
||||
83
Algorithm/Selection/OptionChainedUniverseSelectionModel.cs
Normal file
83
Algorithm/Selection/OptionChainedUniverseSelectionModel.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.Linq;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Algorithm.Framework.Selection;
|
||||
|
||||
namespace QuantConnect.Algorithm.Selection
|
||||
{
|
||||
/// <summary>
|
||||
/// This universe selection model will chain to the security changes of a given <see cref="Universe"/> selection
|
||||
/// output and create a new <see cref="OptionChainUniverse"/> for each of them
|
||||
/// </summary>
|
||||
public class OptionChainedUniverseSelectionModel : UniverseSelectionModel
|
||||
{
|
||||
private DateTime _nextRefreshTimeUtc;
|
||||
private IEnumerable<Symbol> _currentSymbols;
|
||||
private readonly UniverseSettings _universeSettings;
|
||||
private readonly Func<OptionFilterUniverse, OptionFilterUniverse> _optionFilter;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next time the framework should invoke the `CreateUniverses` method to refresh the set of universes.
|
||||
/// </summary>
|
||||
public override DateTime GetNextRefreshTimeUtc() => _nextRefreshTimeUtc;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="OptionChainedUniverseSelectionModel"/>
|
||||
/// </summary>
|
||||
/// <param name="universe">The universe we want to chain to</param>
|
||||
/// <param name="optionFilter">The option filter universe to use</param>
|
||||
/// <param name="universeSettings">Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed</param>
|
||||
public OptionChainedUniverseSelectionModel(Universe universe,
|
||||
Func<OptionFilterUniverse, OptionFilterUniverse> optionFilter,
|
||||
UniverseSettings universeSettings = null)
|
||||
{
|
||||
_optionFilter = optionFilter;
|
||||
_universeSettings = universeSettings;
|
||||
_nextRefreshTimeUtc = DateTime.MaxValue;
|
||||
|
||||
_currentSymbols = Enumerable.Empty<Symbol>();
|
||||
universe.SelectionChanged += (sender, args) =>
|
||||
{
|
||||
// the universe we were watching changed, this will trigger a call to CreateUniverses
|
||||
_nextRefreshTimeUtc = DateTime.MinValue;
|
||||
|
||||
_currentSymbols = ((Universe.SelectionEventArgs)args).CurrentSelection
|
||||
.Select(symbol => Symbol.Create(symbol.Value, SecurityType.Option, symbol.ID.Market, $"?{symbol.Value}"))
|
||||
.ToList();
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the universes for this algorithm. Called once after <see cref="IAlgorithm.Initialize"/>
|
||||
/// </summary>
|
||||
/// <param name="algorithm">The algorithm instance to create universes for</param>
|
||||
/// <returns>The universes to be used by the algorithm</returns>
|
||||
public override IEnumerable<Universe> CreateUniverses(QCAlgorithm algorithm)
|
||||
{
|
||||
_nextRefreshTimeUtc = DateTime.MaxValue;
|
||||
|
||||
foreach (var optionSymbol in _currentSymbols)
|
||||
{
|
||||
yield return algorithm.CreateOptionChain(optionSymbol, _optionFilter, _universeSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
97
Algorithm/Selection/OptionContractUniverse.cs
Normal file
97
Algorithm/Selection/OptionContractUniverse.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.Linq;
|
||||
using QuantConnect.Data;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
|
||||
namespace QuantConnect.Algorithm.Selection
|
||||
{
|
||||
/// <summary>
|
||||
/// This universe will hold single option contracts and their underlying, managing removals and additions
|
||||
/// </summary>
|
||||
public class OptionContractUniverse : UserDefinedUniverse
|
||||
{
|
||||
private readonly HashSet<Symbol> _symbols;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new empty instance
|
||||
/// </summary>
|
||||
/// <param name="configuration">The universe configuration to use</param>
|
||||
/// <param name="universeSettings">The universe settings to use</param>
|
||||
public OptionContractUniverse(SubscriptionDataConfig configuration, UniverseSettings universeSettings)
|
||||
: base(configuration, universeSettings, Time.EndOfTimeTimeSpan,
|
||||
// Argument isn't used since we override 'SelectSymbols'
|
||||
Enumerable.Empty<Symbol>())
|
||||
{
|
||||
_symbols = new HashSet<Symbol>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the symbols defined by the user for this universe
|
||||
/// </summary>
|
||||
/// <param name="utcTime">The current utc time</param>
|
||||
/// <param name="data">The symbols to remain in the universe</param>
|
||||
/// <returns>The data that passes the filter</returns>
|
||||
public override IEnumerable<Symbol> SelectSymbols(DateTime utcTime, BaseDataCollection data)
|
||||
{
|
||||
return _symbols;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event invocator for the <see cref="UserDefinedUniverse.CollectionChanged"/> event
|
||||
/// </summary>
|
||||
/// <param name="args">The notify collection changed event arguments</param>
|
||||
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
if (args.Action == NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
var removedSymbol = (Symbol)args.OldItems[0];
|
||||
_symbols.Remove(removedSymbol);
|
||||
|
||||
// the option has been removed! This can happen when the user manually removed the option contract we remove the underlying
|
||||
if (removedSymbol.SecurityType == SecurityType.Option)
|
||||
{
|
||||
Remove(removedSymbol.Underlying);
|
||||
}
|
||||
}
|
||||
else if (args.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
// QCAlgorithm.AddOptionContract will add both underlying and option contract
|
||||
_symbols.Add((Symbol)args.NewItems[0]);
|
||||
}
|
||||
|
||||
base.OnCollectionChanged(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a user defined universe symbol
|
||||
/// </summary>
|
||||
/// <param name="market">The market</param>
|
||||
/// <param name="securityType">The underlying option security type</param>
|
||||
/// <returns>A symbol for user defined universe of the specified security type and market</returns>
|
||||
public static Symbol CreateSymbol(string market, SecurityType securityType)
|
||||
{
|
||||
var ticker = $"qc-universe-optioncontract-{securityType.SecurityTypeToLower()}-{market.ToLowerInvariant()}";
|
||||
var underlying = Symbol.Create(ticker, securityType, market);
|
||||
var sid = SecurityIdentifier.GenerateOption(SecurityIdentifier.DefaultDate, underlying.ID, market, 0, 0, 0);
|
||||
|
||||
return new Symbol(sid, ticker);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,8 +288,7 @@ logging.captureWarnings(True)"
|
||||
catch (Exception err)
|
||||
{
|
||||
errorMessage = "Algorithm type name not found, or unable to resolve multiple algorithm types to a single type. Please verify algorithm type name matches the algorithm name in the configuration file and that there is one and only one class derived from QCAlgorithm.";
|
||||
errorMessage += err.InnerException == null ? err.Message : err.InnerException.Message;
|
||||
Log.Error($"Loader.TryCreateILAlgorithm(): {errorMessage}");
|
||||
Log.Error($"Loader.TryCreateILAlgorithm(): {errorMessage}\n{err.InnerException ?? err}");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
506
Api/Api.cs
506
Api/Api.cs
@@ -19,8 +19,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json;
|
||||
using QuantConnect.API;
|
||||
using QuantConnect.Data.Market;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using RestSharp;
|
||||
@@ -76,13 +75,15 @@ namespace QuantConnect.Api
|
||||
|
||||
public ProjectResponse CreateProject(string name, Language language)
|
||||
{
|
||||
var request = new RestRequest("projects/create", Method.POST);
|
||||
var request = new RestRequest("projects/create", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.RequestFormat = DataFormat.Json;
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
name = name,
|
||||
language = language
|
||||
name,
|
||||
language
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
ProjectResponse result;
|
||||
@@ -98,10 +99,15 @@ namespace QuantConnect.Api
|
||||
|
||||
public ProjectResponse ReadProject(int projectId)
|
||||
{
|
||||
var request = new RestRequest("projects/read", Method.GET);
|
||||
var request = new RestRequest("projects/read", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.RequestFormat = DataFormat.Json;
|
||||
request.AddParameter("projectId", projectId);
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
ProjectResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
@@ -115,8 +121,11 @@ namespace QuantConnect.Api
|
||||
|
||||
public ProjectResponse ListProjects()
|
||||
{
|
||||
var request = new RestRequest("projects/read", Method.GET);
|
||||
request.RequestFormat = DataFormat.Json;
|
||||
var request = new RestRequest("projects/read", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
ProjectResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
return result;
|
||||
@@ -133,11 +142,17 @@ namespace QuantConnect.Api
|
||||
|
||||
public ProjectFilesResponse AddProjectFile(int projectId, string name, string content)
|
||||
{
|
||||
var request = new RestRequest("files/create", Method.POST);
|
||||
var request = new RestRequest("files/create", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("projectId", projectId);
|
||||
request.AddParameter("name", name);
|
||||
request.AddParameter("content", content);
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId,
|
||||
name,
|
||||
content
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
ProjectFilesResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
@@ -155,11 +170,17 @@ namespace QuantConnect.Api
|
||||
|
||||
public RestResponse UpdateProjectFileName(int projectId, string oldFileName, string newFileName)
|
||||
{
|
||||
var request = new RestRequest("files/update", Method.POST);
|
||||
var request = new RestRequest("files/update", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("projectId", projectId);
|
||||
request.AddParameter("name", oldFileName);
|
||||
request.AddParameter("newName", newFileName);
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId,
|
||||
name = oldFileName,
|
||||
newName = newFileName
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
RestResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
@@ -177,11 +198,17 @@ namespace QuantConnect.Api
|
||||
|
||||
public RestResponse UpdateProjectFileContent(int projectId, string fileName, string newFileContents)
|
||||
{
|
||||
var request = new RestRequest("files/update", Method.POST);
|
||||
var request = new RestRequest("files/update", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("projectId", projectId);
|
||||
request.AddParameter("name", fileName);
|
||||
request.AddParameter("content", newFileContents);
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId,
|
||||
name = fileName,
|
||||
content = newFileContents
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
RestResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
@@ -197,9 +224,15 @@ namespace QuantConnect.Api
|
||||
|
||||
public ProjectFilesResponse ReadProjectFiles(int projectId)
|
||||
{
|
||||
var request = new RestRequest("files/read", Method.GET);
|
||||
var request = new RestRequest("files/read", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("projectId", projectId);
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
ProjectFilesResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
@@ -216,10 +249,16 @@ namespace QuantConnect.Api
|
||||
|
||||
public ProjectFilesResponse ReadProjectFile(int projectId, string fileName)
|
||||
{
|
||||
var request = new RestRequest("files/read", Method.GET);
|
||||
var request = new RestRequest("files/read", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("projectId", projectId);
|
||||
request.AddParameter("name", fileName);
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId,
|
||||
name = fileName
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
ProjectFilesResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
@@ -235,10 +274,16 @@ namespace QuantConnect.Api
|
||||
|
||||
public RestResponse DeleteProjectFile(int projectId, string name)
|
||||
{
|
||||
var request = new RestRequest("files/delete", Method.POST);
|
||||
var request = new RestRequest("files/delete", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("projectId", projectId);
|
||||
request.AddParameter("name", name);
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId,
|
||||
name,
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
RestResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
@@ -253,12 +298,16 @@ namespace QuantConnect.Api
|
||||
|
||||
public RestResponse DeleteProject(int projectId)
|
||||
{
|
||||
var request = new RestRequest("projects/delete", Method.POST);
|
||||
request.RequestFormat = DataFormat.Json;
|
||||
var request = new RestRequest("projects/delete", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId = projectId
|
||||
projectId
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
RestResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
return result;
|
||||
@@ -272,11 +321,16 @@ namespace QuantConnect.Api
|
||||
|
||||
public Compile CreateCompile(int projectId)
|
||||
{
|
||||
var request = new RestRequest("compile/create", Method.POST);
|
||||
var request = new RestRequest("compile/create", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId = projectId
|
||||
projectId
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
Compile result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
return result;
|
||||
@@ -291,10 +345,17 @@ namespace QuantConnect.Api
|
||||
|
||||
public Compile ReadCompile(int projectId, string compileId)
|
||||
{
|
||||
var request = new RestRequest("compile/read", Method.GET);
|
||||
request.RequestFormat = DataFormat.Json;
|
||||
request.AddParameter("projectId", projectId);
|
||||
request.AddParameter("compileId", compileId);
|
||||
var request = new RestRequest("compile/read", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId,
|
||||
compileId
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
Compile result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
return result;
|
||||
@@ -311,10 +372,18 @@ namespace QuantConnect.Api
|
||||
|
||||
public Backtest CreateBacktest(int projectId, string compileId, string backtestName)
|
||||
{
|
||||
var request = new RestRequest("backtests/create", Method.POST);
|
||||
request.AddParameter("projectId", projectId);
|
||||
request.AddParameter("compileId", compileId);
|
||||
request.AddParameter("backtestName", backtestName);
|
||||
var request = new RestRequest("backtests/create", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId,
|
||||
compileId,
|
||||
backtestName
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
Backtest result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
return result;
|
||||
@@ -329,9 +398,17 @@ namespace QuantConnect.Api
|
||||
|
||||
public Backtest ReadBacktest(int projectId, string backtestId)
|
||||
{
|
||||
var request = new RestRequest("backtests/read", Method.GET);
|
||||
request.AddParameter("backtestId", backtestId);
|
||||
request.AddParameter("projectId", projectId);
|
||||
var request = new RestRequest("backtests/read", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId,
|
||||
backtestId
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
Backtest result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
return result;
|
||||
@@ -348,15 +425,19 @@ namespace QuantConnect.Api
|
||||
|
||||
public RestResponse UpdateBacktest(int projectId, string backtestId, string name = "", string note = "")
|
||||
{
|
||||
var request = new RestRequest("backtests/update", Method.POST);
|
||||
request.RequestFormat = DataFormat.Json;
|
||||
var request = new RestRequest("backtests/update", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId = projectId,
|
||||
backtestId = backtestId,
|
||||
name = name,
|
||||
note = note
|
||||
projectId,
|
||||
backtestId,
|
||||
name,
|
||||
note
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
Backtest result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
return result;
|
||||
@@ -370,8 +451,16 @@ namespace QuantConnect.Api
|
||||
|
||||
public BacktestList ListBacktests(int projectId)
|
||||
{
|
||||
var request = new RestRequest("backtests/read", Method.GET);
|
||||
request.AddParameter("projectId", projectId);
|
||||
var request = new RestRequest("backtests/read", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId,
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
BacktestList result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
return result;
|
||||
@@ -386,10 +475,17 @@ namespace QuantConnect.Api
|
||||
|
||||
public RestResponse DeleteBacktest(int projectId, string backtestId)
|
||||
{
|
||||
var request = new RestRequest("backtests/delete", Method.POST);
|
||||
request.RequestFormat = DataFormat.Json;
|
||||
request.AddParameter("backtestId", backtestId);
|
||||
request.AddParameter("projectId", projectId);
|
||||
var request = new RestRequest("backtests/delete", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId,
|
||||
backtestId
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
RestResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
return result;
|
||||
@@ -400,7 +496,7 @@ namespace QuantConnect.Api
|
||||
/// </summary>
|
||||
/// <param name="projectId">Id of the project on QuantConnect</param>
|
||||
/// <param name="compileId">Id of the compilation on QuantConnect</param>
|
||||
/// <param name="serverType">Type of server instance that will run the algorithm</param>
|
||||
/// <param name="nodeId">Id of the node that will run the algorithm</param>
|
||||
/// <param name="baseLiveAlgorithmSettings">Brokerage specific <see cref="BaseLiveAlgorithmSettings">BaseLiveAlgorithmSettings</see>.</param>
|
||||
/// <param name="versionId">The version of the Lean used to run the algorithm.
|
||||
/// -1 is master, however, sometimes this can create problems with live deployments.
|
||||
@@ -409,19 +505,23 @@ namespace QuantConnect.Api
|
||||
|
||||
public LiveAlgorithm CreateLiveAlgorithm(int projectId,
|
||||
string compileId,
|
||||
string serverType,
|
||||
string nodeId,
|
||||
BaseLiveAlgorithmSettings baseLiveAlgorithmSettings,
|
||||
string versionId = "-1")
|
||||
{
|
||||
var request = new RestRequest("live/create", Method.POST);
|
||||
request.AddHeader("Accept", "application/json");
|
||||
request.Parameters.Clear();
|
||||
var body = JsonConvert.SerializeObject(new LiveAlgorithmApiSettingsWrapper(projectId,
|
||||
compileId,
|
||||
serverType,
|
||||
baseLiveAlgorithmSettings,
|
||||
versionId));
|
||||
request.AddParameter("application/json", body, ParameterType.RequestBody);
|
||||
var request = new RestRequest("live/create", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(
|
||||
new LiveAlgorithmApiSettingsWrapper
|
||||
(projectId,
|
||||
compileId,
|
||||
nodeId,
|
||||
baseLiveAlgorithmSettings,
|
||||
versionId)
|
||||
), ParameterType.RequestBody);
|
||||
|
||||
LiveAlgorithm result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
@@ -451,18 +551,26 @@ namespace QuantConnect.Api
|
||||
"The Api only supports Algorithm Statuses of Running, Stopped, RuntimeError and Liquidated");
|
||||
}
|
||||
|
||||
var request = new RestRequest("live/read", Method.GET);
|
||||
|
||||
if (status.HasValue)
|
||||
var request = new RestRequest("live/read", Method.POST)
|
||||
{
|
||||
request.AddParameter("status", status.ToString());
|
||||
}
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
var epochStartTime = startTime == null ? 0 : Time.DateTimeToUnixTimeStamp(startTime.Value);
|
||||
var epochEndTime = endTime == null ? Time.DateTimeToUnixTimeStamp(DateTime.UtcNow) : Time.DateTimeToUnixTimeStamp(endTime.Value);
|
||||
|
||||
request.AddParameter("start", epochStartTime);
|
||||
request.AddParameter("end", epochEndTime);
|
||||
JObject obj = new JObject
|
||||
{
|
||||
{ "start", epochStartTime },
|
||||
{ "end", epochEndTime }
|
||||
};
|
||||
|
||||
if (status.HasValue)
|
||||
{
|
||||
obj.Add("status", status.ToString());
|
||||
}
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(obj), ParameterType.RequestBody);
|
||||
|
||||
LiveList result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
@@ -478,9 +586,17 @@ namespace QuantConnect.Api
|
||||
|
||||
public LiveAlgorithmResults ReadLiveAlgorithm(int projectId, string deployId)
|
||||
{
|
||||
var request = new RestRequest("live/read", Method.GET);
|
||||
request.AddParameter("projectId", projectId);
|
||||
request.AddParameter("deployId", deployId);
|
||||
var request = new RestRequest("live/read", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId,
|
||||
deployId
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
LiveAlgorithmResults result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
return result;
|
||||
@@ -494,9 +610,16 @@ namespace QuantConnect.Api
|
||||
|
||||
public RestResponse LiquidateLiveAlgorithm(int projectId)
|
||||
{
|
||||
var request = new RestRequest("live/update/liquidate", Method.POST);
|
||||
request.RequestFormat = DataFormat.Json;
|
||||
request.AddParameter("projectId", projectId);
|
||||
var request = new RestRequest("live/update/liquidate", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
RestResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
return result;
|
||||
@@ -510,9 +633,16 @@ namespace QuantConnect.Api
|
||||
|
||||
public RestResponse StopLiveAlgorithm(int projectId)
|
||||
{
|
||||
var request = new RestRequest("live/update/stop", Method.POST);
|
||||
request.RequestFormat = DataFormat.Json;
|
||||
request.AddParameter("projectId", projectId);
|
||||
var request = new RestRequest("live/update/stop", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
projectId
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
RestResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
return result;
|
||||
@@ -532,13 +662,19 @@ namespace QuantConnect.Api
|
||||
var epochStartTime = startTime == null ? 0 : Time.DateTimeToUnixTimeStamp(startTime.Value);
|
||||
var epochEndTime = endTime == null ? Time.DateTimeToUnixTimeStamp(DateTime.UtcNow) : Time.DateTimeToUnixTimeStamp(endTime.Value);
|
||||
|
||||
var request = new RestRequest("live/read/log", Method.GET);
|
||||
var request = new RestRequest("live/read/log", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("format", "json");
|
||||
request.AddParameter("projectId", projectId);
|
||||
request.AddParameter("algorithmId", algorithmId);
|
||||
request.AddParameter("start", epochStartTime);
|
||||
request.AddParameter("end", epochEndTime);
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
format = "json",
|
||||
projectId,
|
||||
algorithmId,
|
||||
start = epochStartTime,
|
||||
end = epochEndTime
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
LiveLog result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
@@ -555,14 +691,20 @@ namespace QuantConnect.Api
|
||||
|
||||
public Link ReadDataLink(Symbol symbol, Resolution resolution, DateTime date)
|
||||
{
|
||||
var request = new RestRequest("data/read", Method.GET);
|
||||
var request = new RestRequest("data/read", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("format", "link");
|
||||
request.AddParameter("ticker", symbol.Value.ToLowerInvariant());
|
||||
request.AddParameter("type", symbol.ID.SecurityType.ToLower());
|
||||
request.AddParameter("market", symbol.ID.Market);
|
||||
request.AddParameter("resolution", resolution);
|
||||
request.AddParameter("date", date.ToStringInvariant("yyyyMMdd"));
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
format = "link",
|
||||
ticker = symbol.Value.ToLowerInvariant(),
|
||||
type = symbol.ID.SecurityType.ToLower(),
|
||||
market = symbol.ID.Market,
|
||||
resolution = resolution.ToString(),
|
||||
date = date.ToStringInvariant("yyyyMMdd")
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
Link result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
@@ -577,44 +719,22 @@ namespace QuantConnect.Api
|
||||
/// <returns><see cref="BacktestReport"/></returns>
|
||||
public BacktestReport ReadBacktestReport(int projectId, string backtestId)
|
||||
{
|
||||
var request = new RestRequest("backtests/read/report", Method.POST);
|
||||
request.AddParameter("backtestId", backtestId);
|
||||
request.AddParameter("projectId", projectId);
|
||||
var request = new RestRequest("backtests/read/report", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
backtestId,
|
||||
projectId
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
BacktestReport report;
|
||||
ApiConnection.TryRequest(request, out report);
|
||||
return report;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will get the prices for requested symbols
|
||||
/// </summary>
|
||||
/// <param name="symbols">Symbols for which the price is requested</param>
|
||||
/// <returns><see cref="Prices"/></returns>
|
||||
public PricesList ReadPrices(IEnumerable<Symbol> symbols)
|
||||
{
|
||||
var symbolByID = new Dictionary<string, Symbol>();
|
||||
foreach (var symbol in symbols)
|
||||
{
|
||||
symbolByID[symbol.ID.ToString()] = symbol;
|
||||
}
|
||||
|
||||
var request = new RestRequest("prices", Method.POST);
|
||||
var symbolsToRequest = string.Join(",", symbolByID.Keys);
|
||||
request.AddParameter("symbols", symbolsToRequest);
|
||||
|
||||
PricesList pricesList;
|
||||
if (ApiConnection.TryRequest(request, out pricesList))
|
||||
{
|
||||
foreach (var price in pricesList.Prices)
|
||||
{
|
||||
price.Symbol = symbolByID[price.SymbolID];
|
||||
}
|
||||
}
|
||||
|
||||
return pricesList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to download and save the data purchased through QuantConnect
|
||||
/// </summary>
|
||||
@@ -705,54 +825,6 @@ namespace QuantConnect.Api
|
||||
//
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all split events between the specified times. From and to are inclusive.
|
||||
/// </summary>
|
||||
/// <param name="from">The first date to get splits for</param>
|
||||
/// <param name="to">The last date to get splits for</param>
|
||||
/// <returns>A list of all splits in the specified range</returns>
|
||||
public List<Data.Market.Split> GetSplits(DateTime from, DateTime to)
|
||||
{
|
||||
var request = new RestRequest("splits", Method.POST);
|
||||
request.AddParameter("from", from.ToStringInvariant("yyyyMMdd"));
|
||||
request.AddParameter("to", from.ToStringInvariant("yyyyMMdd"));
|
||||
|
||||
SplitList splits;
|
||||
ApiConnection.TryRequest(request, out splits);
|
||||
|
||||
return splits.Splits.Select(s => new Data.Market.Split(
|
||||
s.Symbol,
|
||||
s.Date,
|
||||
s.ReferencePrice,
|
||||
s.SplitFactor,
|
||||
SplitType.SplitOccurred)
|
||||
).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all dividend events between the specified times. From and to are inclusive.
|
||||
/// </summary>
|
||||
/// <param name="from">The first date to get dividend for</param>
|
||||
/// <param name="to">The last date to get dividend for</param>
|
||||
/// <returns>A list of all dividend in the specified range</returns>
|
||||
public List<Data.Market.Dividend> GetDividends(DateTime from, DateTime to)
|
||||
{
|
||||
var request = new RestRequest("dividends", Method.POST);
|
||||
request.AddParameter("from", from.ToStringInvariant("yyyyMMdd"));
|
||||
request.AddParameter("to", from.ToStringInvariant("yyyyMMdd"));
|
||||
|
||||
DividendList dividends;
|
||||
ApiConnection.TryRequest(request, out dividends);
|
||||
|
||||
return dividends.Dividends.Select(s => new Data.Market.Dividend(
|
||||
s.Symbol,
|
||||
s.Date,
|
||||
s.DividendPerShare,
|
||||
s.ReferencePrice)
|
||||
).ToList();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Local implementation for downloading data to algorithms
|
||||
/// </summary>
|
||||
@@ -808,32 +880,44 @@ namespace QuantConnect.Api
|
||||
/// <param name="name">The name of the new node</param>
|
||||
/// <param name="organizationId">ID of the organization</param>
|
||||
/// <param name="sku"><see cref="SKU"/> Object representing configuration</param>
|
||||
/// <returns>Returns <see cref="CreatedNode"/> which contains API response and
|
||||
/// <returns>Returns <see cref="CreatedNode"/> which contains API response and
|
||||
/// <see cref="Node"/></returns>
|
||||
public CreatedNode CreateNode(string name, string organizationId, SKU sku)
|
||||
{
|
||||
var request = new RestRequest("nodes/create", Method.POST);
|
||||
request.AddParameter("name", name);
|
||||
request.AddParameter("organizationId", organizationId);
|
||||
request.AddParameter("sku", sku.ToString());
|
||||
var request = new RestRequest("nodes/create", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
name,
|
||||
organizationId,
|
||||
sku = sku.ToString()
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
CreatedNode result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the nodes associated with the organization, creating a
|
||||
/// Reads the nodes associated with the organization, creating a
|
||||
/// <see cref="NodeList"/> for the response
|
||||
/// </summary>
|
||||
/// <param name="organizationId">ID of the organization</param>
|
||||
/// <returns><see cref="NodeList"/> containing Backtest, Research, and Live Nodes</returns>
|
||||
public NodeList ReadNodes(string organizationId)
|
||||
{
|
||||
var request = new RestRequest("nodes/read", Method.POST);
|
||||
request.RequestFormat = DataFormat.Json;
|
||||
request.AddParameter("organizationId", organizationId);
|
||||
var request = new RestRequest("nodes/read", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
organizationId,
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
NodeList result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
@@ -849,11 +933,17 @@ namespace QuantConnect.Api
|
||||
/// <returns><see cref="RestResponse"/> containing success response and errors</returns>
|
||||
public RestResponse UpdateNode(string nodeId, string newName, string organizationId)
|
||||
{
|
||||
var request = new RestRequest("nodes/update", Method.POST);
|
||||
request.RequestFormat = DataFormat.Json;
|
||||
request.AddParameter("nodeId", nodeId);
|
||||
request.AddParameter("name", newName);
|
||||
request.AddParameter("organizationId", organizationId);
|
||||
var request = new RestRequest("nodes/update", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
nodeId,
|
||||
name = newName,
|
||||
organizationId
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
RestResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
@@ -868,10 +958,16 @@ namespace QuantConnect.Api
|
||||
/// <returns><see cref="RestResponse"/> containing success response and errors</returns>
|
||||
public RestResponse DeleteNode(string nodeId, string organizationId)
|
||||
{
|
||||
var request = new RestRequest("nodes/delete", Method.POST);
|
||||
request.RequestFormat = DataFormat.Json;
|
||||
request.AddParameter("nodeId", nodeId);
|
||||
request.AddParameter("organizationId", organizationId);
|
||||
var request = new RestRequest("nodes/delete", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
nodeId,
|
||||
organizationId
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
RestResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
@@ -886,10 +982,16 @@ namespace QuantConnect.Api
|
||||
/// <returns><see cref="RestResponse"/> containing success response and errors</returns>
|
||||
public RestResponse StopNode(string nodeId, string organizationId)
|
||||
{
|
||||
var request = new RestRequest("nodes/stop", Method.POST);
|
||||
request.RequestFormat = DataFormat.Json;
|
||||
request.AddParameter("nodeId", nodeId);
|
||||
request.AddParameter("organizationId", organizationId);
|
||||
var request = new RestRequest("nodes/stop", Method.POST)
|
||||
{
|
||||
RequestFormat = DataFormat.Json
|
||||
};
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(new
|
||||
{
|
||||
nodeId,
|
||||
organizationId
|
||||
}), ParameterType.RequestBody);
|
||||
|
||||
RestResponse result;
|
||||
ApiConnection.TryRequest(request, out result);
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using QuantConnect.API;
|
||||
using QuantConnect.Configuration;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Orders;
|
||||
|
||||
2271
Api/QuantConnect-Platform-2.0.0.yaml
Normal file
2271
Api/QuantConnect-Platform-2.0.0.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -110,6 +110,11 @@ namespace QuantConnect.Brokerages.Alpaca
|
||||
/// </summary>
|
||||
public override bool IsConnected => _sockClient.IsConnected;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the brokerage account's base currency
|
||||
/// </summary>
|
||||
public override string AccountBaseCurrency => Currencies.USD;
|
||||
|
||||
/// <summary>
|
||||
/// Connects the client to the broker's remote servers
|
||||
/// </summary>
|
||||
@@ -117,6 +122,8 @@ namespace QuantConnect.Brokerages.Alpaca
|
||||
{
|
||||
if (IsConnected) return;
|
||||
|
||||
AccountBaseCurrency = GetAccountBaseCurrency();
|
||||
|
||||
_sockClient.Connect();
|
||||
}
|
||||
|
||||
@@ -153,8 +160,7 @@ namespace QuantConnect.Brokerages.Alpaca
|
||||
|
||||
return new List<CashAmount>
|
||||
{
|
||||
new CashAmount(balance.TradableCash,
|
||||
Currencies.USD)
|
||||
new CashAmount(balance.TradableCash, balance.Currency)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -293,6 +299,19 @@ namespace QuantConnect.Brokerages.Alpaca
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the account base currency
|
||||
/// </summary>
|
||||
private string GetAccountBaseCurrency()
|
||||
{
|
||||
CheckRateLimiting();
|
||||
|
||||
var task = _alpacaTradingClient.GetAccountAsync();
|
||||
var balance = task.SynchronouslyAwaitTaskResult();
|
||||
|
||||
return balance.Currency;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,19 +54,8 @@ namespace QuantConnect.Brokerages
|
||||
/// <returns>The latest price</returns>
|
||||
public decimal GetLastPrice(Symbol symbol)
|
||||
{
|
||||
var result = _api.ReadPrices(new[] { symbol });
|
||||
if (!result.Success)
|
||||
{
|
||||
throw new Exception($"ReadPrices error: {string.Join(" - ", result.Errors)}");
|
||||
}
|
||||
|
||||
var priceData = result.Prices.FirstOrDefault(x => x.Symbol == symbol);
|
||||
if (priceData == null)
|
||||
{
|
||||
throw new Exception($"No price data available for symbol: {symbol.Value}");
|
||||
}
|
||||
|
||||
return priceData.Price;
|
||||
//NOP ReadPrices endpoint has been removed
|
||||
throw new InvalidOperationException("Prices endpoint is no longer supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,7 +430,7 @@ namespace QuantConnect.Brokerages.Backtesting
|
||||
OnOrderEvent(fill);
|
||||
}
|
||||
|
||||
if (order.Type == OrderType.OptionExercise)
|
||||
if (fill.IsAssignment)
|
||||
{
|
||||
fill.Message = order.Tag;
|
||||
OnOptionPositionAssigned(fill);
|
||||
@@ -474,7 +474,8 @@ namespace QuantConnect.Brokerages.Backtesting
|
||||
|
||||
_pendingOptionAssignments.Add(option.Symbol);
|
||||
|
||||
var request = new SubmitOrderRequest(OrderType.OptionExercise, option.Type, option.Symbol, -quantity, 0m, 0m, Algorithm.UtcTime, "Simulated option assignment before expiration");
|
||||
// assignments always cause a positive change to option contract holdings
|
||||
var request = new SubmitOrderRequest(OrderType.OptionExercise, option.Type, option.Symbol, Math.Abs(quantity), 0m, 0m, Algorithm.UtcTime, "Simulated option assignment before expiration");
|
||||
|
||||
var ticket = Algorithm.Transactions.ProcessRequest(request);
|
||||
Log.Trace($"BacktestingBrokerage.ActivateOptionAssignment(): OrderId: {ticket.OrderId}");
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace QuantConnect.Brokerages.Backtesting
|
||||
// last update time
|
||||
private DateTime _lastUpdate = DateTime.MinValue;
|
||||
private Queue<DateTime> _assignmentScans;
|
||||
private static Random _rand = new Random((int)12345);
|
||||
private readonly Random _rand = new Random(12345);
|
||||
|
||||
/// <summary>
|
||||
/// We generate a list of time points when we would like to run our simulation. we then return true if the time is in the list.
|
||||
@@ -124,12 +124,12 @@ namespace QuantConnect.Brokerages.Backtesting
|
||||
|
||||
Func<Symbol, bool> deepITM = symbol =>
|
||||
{
|
||||
var undelyingPrice = algorithm.Securities[symbol.Underlying].Close;
|
||||
var underlyingPrice = algorithm.Securities[symbol.Underlying].Close;
|
||||
|
||||
var result =
|
||||
symbol.ID.OptionRight == OptionRight.Call ?
|
||||
(undelyingPrice - symbol.ID.StrikePrice) / undelyingPrice > _deepITM :
|
||||
(symbol.ID.StrikePrice - undelyingPrice) / undelyingPrice > _deepITM;
|
||||
symbol.ID.OptionRight == OptionRight.Call
|
||||
? (underlyingPrice - symbol.ID.StrikePrice) / underlyingPrice > _deepITM
|
||||
: (symbol.ID.StrikePrice - underlyingPrice) / underlyingPrice > _deepITM;
|
||||
|
||||
return result;
|
||||
};
|
||||
@@ -168,13 +168,13 @@ namespace QuantConnect.Brokerages.Backtesting
|
||||
|
||||
// we are interested in underlying bid price if we exercise calls and want to sell the underlying immediately.
|
||||
// we are interested in underlying ask price if we exercise puts
|
||||
var underlyingPrice = option.Symbol.ID.OptionRight == OptionRight.Call ?
|
||||
underlying.BidPrice :
|
||||
underlying.AskPrice;
|
||||
var underlyingPrice = option.Symbol.ID.OptionRight == OptionRight.Call
|
||||
? underlying.BidPrice
|
||||
: underlying.AskPrice;
|
||||
|
||||
var underlyingQuantity = option.Symbol.ID.OptionRight == OptionRight.Call ?
|
||||
option.GetExerciseQuantity((int)holding.AbsoluteQuantity) :
|
||||
-option.GetExerciseQuantity((int)holding.AbsoluteQuantity);
|
||||
// quantity is normally negative algo's holdings, but since we're modeling the contract holder (counter-party)
|
||||
// it's negative THEIR holdings. holding.Quantity is negative, so if counter-party exercises, they would reduce holdings
|
||||
var underlyingQuantity = option.GetExerciseQuantity(holding.Quantity);
|
||||
|
||||
// Scenario 1 (base): we just close option position
|
||||
var marketOrder1 = new MarketOrder(option.Symbol, -holding.Quantity, option.LocalTime.ConvertToUtc(option.Exchange.TimeZone));
|
||||
|
||||
226
Brokerages/Binance/BinanceBrokerage.Messaging.cs
Normal file
226
Brokerages/Binance/BinanceBrokerage.Messaging.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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.Brokerages.Binance.Messages;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Orders.Fees;
|
||||
using QuantConnect.Securities;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using QuantConnect.Data;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
public partial class BinanceBrokerage
|
||||
{
|
||||
private readonly ConcurrentQueue<WebSocketMessage> _messageBuffer = new ConcurrentQueue<WebSocketMessage>();
|
||||
private volatile bool _streamLocked;
|
||||
private readonly IDataAggregator _aggregator;
|
||||
|
||||
/// <summary>
|
||||
/// Locking object for the Ticks list in the data queue handler
|
||||
/// </summary>
|
||||
protected readonly object TickLocker = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Lock the streaming processing while we're sending orders as sometimes they fill before the REST call returns.
|
||||
/// </summary>
|
||||
private void LockStream()
|
||||
{
|
||||
_streamLocked = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unlock stream and process all backed up messages.
|
||||
/// </summary>
|
||||
private void UnlockStream()
|
||||
{
|
||||
while (_messageBuffer.Any())
|
||||
{
|
||||
WebSocketMessage e;
|
||||
_messageBuffer.TryDequeue(out e);
|
||||
|
||||
OnMessageImpl(e);
|
||||
}
|
||||
|
||||
// Once dequeued in order; unlock stream.
|
||||
_streamLocked = false;
|
||||
}
|
||||
|
||||
private void WithLockedStream(Action code)
|
||||
{
|
||||
try
|
||||
{
|
||||
LockStream();
|
||||
code();
|
||||
}
|
||||
finally
|
||||
{
|
||||
UnlockStream();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMessageImpl(WebSocketMessage e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var obj = JObject.Parse(e.Message);
|
||||
|
||||
var objError = obj["error"];
|
||||
if (objError != null)
|
||||
{
|
||||
var error = objError.ToObject<ErrorMessage>();
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, error.Code, error.Message));
|
||||
return;
|
||||
}
|
||||
|
||||
var objData = obj;
|
||||
|
||||
var objEventType = objData["e"];
|
||||
if (objEventType != null)
|
||||
{
|
||||
var eventType = objEventType.ToObject<string>();
|
||||
|
||||
switch (eventType)
|
||||
{
|
||||
case "executionReport":
|
||||
var upd = objData.ToObject<Execution>();
|
||||
if (upd.ExecutionType.Equals("TRADE", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
OnFillOrder(upd);
|
||||
}
|
||||
break;
|
||||
|
||||
case "trade":
|
||||
var trade = objData.ToObject<Trade>();
|
||||
EmitTradeTick(
|
||||
_symbolMapper.GetLeanSymbol(trade.Symbol),
|
||||
Time.UnixMillisecondTimeStampToDateTime(trade.Time),
|
||||
trade.Price,
|
||||
trade.Quantity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (objData["u"] != null)
|
||||
{
|
||||
var quote = objData.ToObject<BestBidAskQuote>();
|
||||
EmitQuoteTick(
|
||||
_symbolMapper.GetLeanSymbol(quote.Symbol),
|
||||
quote.BestBidPrice,
|
||||
quote.BestBidSize,
|
||||
quote.BestAskPrice,
|
||||
quote.BestAskSize);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, -1, $"Parsing wss message failed. Data: {e.Message} Exception: {exception}"));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitQuoteTick(Symbol symbol, decimal bidPrice, decimal bidSize, decimal askPrice, decimal askSize)
|
||||
{
|
||||
var tick = new Tick
|
||||
{
|
||||
AskPrice = askPrice,
|
||||
BidPrice = bidPrice,
|
||||
Time = DateTime.UtcNow,
|
||||
Symbol = symbol,
|
||||
TickType = TickType.Quote,
|
||||
AskSize = askSize,
|
||||
BidSize = bidSize
|
||||
};
|
||||
tick.SetValue();
|
||||
|
||||
lock (TickLocker)
|
||||
{
|
||||
_aggregator.Update(tick);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitTradeTick(Symbol symbol, DateTime time, decimal price, decimal quantity)
|
||||
{
|
||||
var tick = new Tick
|
||||
{
|
||||
Symbol = symbol,
|
||||
Value = price,
|
||||
Quantity = Math.Abs(quantity),
|
||||
Time = time,
|
||||
TickType = TickType.Trade
|
||||
};
|
||||
|
||||
lock (TickLocker)
|
||||
{
|
||||
_aggregator.Update(tick);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFillOrder(Execution data)
|
||||
{
|
||||
try
|
||||
{
|
||||
var order = FindOrderByExternalId(data.OrderId);
|
||||
if (order == null)
|
||||
{
|
||||
// not our order, nothing else to do here
|
||||
return;
|
||||
}
|
||||
|
||||
var fillPrice = data.LastExecutedPrice;
|
||||
var fillQuantity = data.Direction == OrderDirection.Sell ? -data.LastExecutedQuantity : data.LastExecutedQuantity;
|
||||
var updTime = Time.UnixMillisecondTimeStampToDateTime(data.TransactionTime);
|
||||
var orderFee = new OrderFee(new CashAmount(data.Fee, data.FeeCurrency));
|
||||
var status = ConvertOrderStatus(data.OrderStatus);
|
||||
var orderEvent = new OrderEvent
|
||||
(
|
||||
order.Id, order.Symbol, updTime, status,
|
||||
data.Direction, fillPrice, fillQuantity,
|
||||
orderFee, $"Binance Order Event {data.Direction}"
|
||||
);
|
||||
|
||||
if (status == OrderStatus.Filled)
|
||||
{
|
||||
Orders.Order outOrder;
|
||||
CachedOrderIDs.TryRemove(order.Id, out outOrder);
|
||||
}
|
||||
|
||||
OnOrderEvent(orderEvent);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private Orders.Order FindOrderByExternalId(string brokerId)
|
||||
{
|
||||
var order = CachedOrderIDs
|
||||
.FirstOrDefault(o => o.Value.BrokerId.Contains(brokerId))
|
||||
.Value;
|
||||
if (order == null)
|
||||
{
|
||||
order = _algorithm.Transactions.GetOrderByBrokerageId(brokerId);
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Brokerages/Binance/BinanceBrokerage.Utility.cs
Normal file
53
Brokerages/Binance/BinanceBrokerage.Utility.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.Orders;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Binance utility methods
|
||||
/// </summary>
|
||||
public partial class BinanceBrokerage
|
||||
{
|
||||
private static OrderStatus ConvertOrderStatus(string raw)
|
||||
{
|
||||
switch (raw.LazyToUpper())
|
||||
{
|
||||
case "NEW":
|
||||
return OrderStatus.New;
|
||||
|
||||
case "PARTIALLY_FILLED":
|
||||
return OrderStatus.PartiallyFilled;
|
||||
|
||||
case "FILLED":
|
||||
return OrderStatus.Filled;
|
||||
|
||||
case "PENDING_CANCEL":
|
||||
return OrderStatus.CancelPending;
|
||||
|
||||
case "CANCELED":
|
||||
return OrderStatus.Canceled;
|
||||
|
||||
case "REJECTED":
|
||||
case "EXPIRED":
|
||||
return OrderStatus.Invalid;
|
||||
|
||||
default:
|
||||
return OrderStatus.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
491
Brokerages/Binance/BinanceBrokerage.cs
Normal file
491
Brokerages/Binance/BinanceBrokerage.cs
Normal file
@@ -0,0 +1,491 @@
|
||||
/*
|
||||
* 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.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Packets;
|
||||
using QuantConnect.Securities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Newtonsoft.Json;
|
||||
using QuantConnect.Util;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Binance brokerage implementation
|
||||
/// </summary>
|
||||
[BrokerageFactory(typeof(BinanceBrokerageFactory))]
|
||||
public partial class BinanceBrokerage : BaseWebsocketsBrokerage, IDataQueueHandler
|
||||
{
|
||||
private const string WebSocketBaseUrl = "wss://stream.binance.com:9443/ws";
|
||||
|
||||
private readonly IAlgorithm _algorithm;
|
||||
private readonly BinanceSymbolMapper _symbolMapper = new BinanceSymbolMapper();
|
||||
|
||||
private readonly RateGate _webSocketRateLimiter = new RateGate(5, TimeSpan.FromSeconds(1));
|
||||
private long _lastRequestId;
|
||||
|
||||
private readonly Timer _keepAliveTimer;
|
||||
private readonly Timer _reconnectTimer;
|
||||
private readonly BinanceRestApiClient _apiClient;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for brokerage
|
||||
/// </summary>
|
||||
/// <param name="apiKey">api key</param>
|
||||
/// <param name="apiSecret">api secret</param>
|
||||
/// <param name="algorithm">the algorithm instance is required to retrieve account type</param>
|
||||
/// <param name="aggregator">the aggregator for consolidating ticks</param>
|
||||
public BinanceBrokerage(string apiKey, string apiSecret, IAlgorithm algorithm, IDataAggregator aggregator)
|
||||
: base(WebSocketBaseUrl, new WebSocketClientWrapper(), null, apiKey, apiSecret, "Binance")
|
||||
{
|
||||
_algorithm = algorithm;
|
||||
_aggregator = aggregator;
|
||||
|
||||
var subscriptionManager = new EventBasedDataQueueHandlerSubscriptionManager();
|
||||
subscriptionManager.SubscribeImpl += (s, t) =>
|
||||
{
|
||||
Subscribe(s);
|
||||
return true;
|
||||
};
|
||||
subscriptionManager.UnsubscribeImpl += (s, t) => Unsubscribe(s);
|
||||
|
||||
SubscriptionManager = subscriptionManager;
|
||||
|
||||
_apiClient = new BinanceRestApiClient(
|
||||
_symbolMapper,
|
||||
algorithm?.Portfolio,
|
||||
apiKey,
|
||||
apiSecret);
|
||||
|
||||
_apiClient.OrderSubmit += (s, e) => OnOrderSubmit(e);
|
||||
_apiClient.OrderStatusChanged += (s, e) => OnOrderEvent(e);
|
||||
_apiClient.Message += (s, e) => OnMessage(e);
|
||||
|
||||
// User data streams will close after 60 minutes. It's recommended to send a ping about every 30 minutes.
|
||||
// Source: https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#pingkeep-alive-a-listenkey
|
||||
_keepAliveTimer = new Timer
|
||||
{
|
||||
// 30 minutes
|
||||
Interval = 30 * 60 * 1000
|
||||
};
|
||||
_keepAliveTimer.Elapsed += (s, e) => _apiClient.SessionKeepAlive();
|
||||
|
||||
WebSocket.Open += (s, e) => { _keepAliveTimer.Start(); };
|
||||
WebSocket.Closed += (s, e) => { _keepAliveTimer.Stop(); };
|
||||
|
||||
// A single connection to stream.binance.com is only valid for 24 hours; expect to be disconnected at the 24 hour mark
|
||||
// Source: https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#general-wss-information
|
||||
_reconnectTimer = new Timer
|
||||
{
|
||||
// 23.5 hours
|
||||
Interval = 23.5 * 60 * 60 * 1000
|
||||
};
|
||||
_reconnectTimer.Elapsed += (s, e) =>
|
||||
{
|
||||
Log.Trace("Daily websocket restart: disconnect");
|
||||
Disconnect();
|
||||
|
||||
Log.Trace("Daily websocket restart: connect");
|
||||
Connect();
|
||||
};
|
||||
}
|
||||
|
||||
#region IBrokerage
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the websocket connection is connected or in the process of connecting
|
||||
/// </summary>
|
||||
public override bool IsConnected => WebSocket.IsOpen;
|
||||
|
||||
/// <summary>
|
||||
/// Creates wss connection
|
||||
/// </summary>
|
||||
public override void Connect()
|
||||
{
|
||||
if (IsConnected)
|
||||
return;
|
||||
|
||||
_apiClient.CreateListenKey();
|
||||
_reconnectTimer.Start();
|
||||
|
||||
WebSocket.Initialize($"{WebSocketBaseUrl}/{_apiClient.SessionId}");
|
||||
|
||||
base.Connect();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the websockets connection
|
||||
/// </summary>
|
||||
public override void Disconnect()
|
||||
{
|
||||
_reconnectTimer.Stop();
|
||||
|
||||
WebSocket?.Close();
|
||||
_apiClient.StopSession();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all open positions
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override List<Holding> GetAccountHoldings()
|
||||
{
|
||||
return _apiClient.GetAccountHoldings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total account cash balance for specified account type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override List<CashAmount> GetCashBalance()
|
||||
{
|
||||
var account = _apiClient.GetCashBalance();
|
||||
var balances = account.Balances?.Where(balance => balance.Amount > 0).ToList();
|
||||
if (balances == null || !balances.Any())
|
||||
return new List<CashAmount>();
|
||||
|
||||
return balances
|
||||
.Select(b => new CashAmount(b.Amount, b.Asset.LazyToUpper()))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all orders not yet closed
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override List<Order> GetOpenOrders()
|
||||
{
|
||||
var orders = _apiClient.GetOpenOrders();
|
||||
List<Order> list = new List<Order>();
|
||||
foreach (var item in orders)
|
||||
{
|
||||
Order order;
|
||||
switch (item.Type.LazyToUpper())
|
||||
{
|
||||
case "MARKET":
|
||||
order = new MarketOrder { Price = item.Price };
|
||||
break;
|
||||
case "LIMIT":
|
||||
case "LIMIT_MAKER":
|
||||
order = new LimitOrder { LimitPrice = item.Price };
|
||||
break;
|
||||
case "STOP_LOSS":
|
||||
case "TAKE_PROFIT":
|
||||
order = new StopMarketOrder { StopPrice = item.StopPrice, Price = item.Price };
|
||||
break;
|
||||
case "STOP_LOSS_LIMIT":
|
||||
case "TAKE_PROFIT_LIMIT":
|
||||
order = new StopLimitOrder { StopPrice = item.StopPrice, LimitPrice = item.Price };
|
||||
break;
|
||||
default:
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, -1,
|
||||
"BinanceBrokerage.GetOpenOrders: Unsupported order type returned from brokerage: " + item.Type));
|
||||
continue;
|
||||
}
|
||||
|
||||
order.Quantity = item.Quantity;
|
||||
order.BrokerId = new List<string> { item.Id };
|
||||
order.Symbol = _symbolMapper.GetLeanSymbol(item.Symbol);
|
||||
order.Time = Time.UnixMillisecondTimeStampToDateTime(item.Time);
|
||||
order.Status = ConvertOrderStatus(item.Status);
|
||||
order.Price = item.Price;
|
||||
|
||||
if (order.Status.IsOpen())
|
||||
{
|
||||
var cached = CachedOrderIDs.Where(c => c.Value.BrokerId.Contains(order.BrokerId.First())).ToList();
|
||||
if (cached.Any())
|
||||
{
|
||||
CachedOrderIDs[cached.First().Key] = order;
|
||||
}
|
||||
}
|
||||
|
||||
list.Add(order);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
var submitted = false;
|
||||
|
||||
WithLockedStream(() =>
|
||||
{
|
||||
submitted = _apiClient.PlaceOrder(order);
|
||||
});
|
||||
|
||||
return submitted;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
throw new NotSupportedException("BinanceBrokerage.UpdateOrder: Order update not supported. Please cancel and re-create.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the order with the specified ID
|
||||
/// </summary>
|
||||
/// <param name="order">The order to cancel</param>
|
||||
/// <returns>True if the request was submitted for cancellation, false otherwise</returns>
|
||||
public override bool CancelOrder(Order order)
|
||||
{
|
||||
var submitted = false;
|
||||
|
||||
WithLockedStream(() =>
|
||||
{
|
||||
submitted = _apiClient.CancelOrder(order);
|
||||
});
|
||||
|
||||
return submitted;
|
||||
}
|
||||
|
||||
/// <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(Data.HistoryRequest request)
|
||||
{
|
||||
if (request.Resolution == Resolution.Tick || request.Resolution == Resolution.Second)
|
||||
{
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidResolution",
|
||||
$"{request.Resolution} resolution is not supported, no history returned"));
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (request.TickType != TickType.Trade)
|
||||
{
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidTickType",
|
||||
$"{request.TickType} tick type not supported, no history returned"));
|
||||
yield break;
|
||||
}
|
||||
|
||||
var period = request.Resolution.ToTimeSpan();
|
||||
|
||||
foreach (var kline in _apiClient.GetHistory(request))
|
||||
{
|
||||
yield return new TradeBar()
|
||||
{
|
||||
Time = Time.UnixMillisecondTimeStampToDateTime(kline.OpenTime),
|
||||
Symbol = request.Symbol,
|
||||
Low = kline.Low,
|
||||
High = kline.High,
|
||||
Open = kline.Open,
|
||||
Close = kline.Close,
|
||||
Volume = kline.Volume,
|
||||
Value = kline.Close,
|
||||
DataType = MarketDataType.TradeBar,
|
||||
Period = period,
|
||||
EndTime = Time.UnixMillisecondTimeStampToDateTime(kline.OpenTime + (long)period.TotalMilliseconds)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wss message handler
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
public override void OnMessage(object sender, WebSocketMessage e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_streamLocked)
|
||||
{
|
||||
_messageBuffer.Enqueue(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
Log.Error(err);
|
||||
}
|
||||
|
||||
OnMessageImpl(e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDataQueueHandler
|
||||
|
||||
/// <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>
|
||||
/// 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>
|
||||
/// Checks if this brokerage supports the specified symbol
|
||||
/// </summary>
|
||||
/// <param name="symbol">The symbol</param>
|
||||
/// <returns>returns true if brokerage supports the specified symbol; otherwise false</returns>
|
||||
private static bool CanSubscribe(Symbol symbol)
|
||||
{
|
||||
return !symbol.Value.Contains("UNIVERSE") &&
|
||||
symbol.SecurityType == SecurityType.Crypto;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
_keepAliveTimer.DisposeSafely();
|
||||
_reconnectTimer.DisposeSafely();
|
||||
_apiClient.DisposeSafely();
|
||||
_webSocketRateLimiter.DisposeSafely();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to the requested symbols (using an individual streaming channel)
|
||||
/// </summary>
|
||||
/// <param name="symbols">The list of symbols to subscribe</param>
|
||||
public override void Subscribe(IEnumerable<Symbol> symbols)
|
||||
{
|
||||
foreach (var symbol in symbols)
|
||||
{
|
||||
Send(WebSocket,
|
||||
new
|
||||
{
|
||||
method = "SUBSCRIBE",
|
||||
@params = new[]
|
||||
{
|
||||
$"{symbol.Value.ToLowerInvariant()}@trade",
|
||||
$"{symbol.Value.ToLowerInvariant()}@bookTicker"
|
||||
},
|
||||
id = GetNextRequestId()
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends current subscriptions
|
||||
/// </summary>
|
||||
private bool Unsubscribe(IEnumerable<Symbol> symbols)
|
||||
{
|
||||
if (WebSocket.IsOpen)
|
||||
{
|
||||
foreach (var symbol in symbols)
|
||||
{
|
||||
Send(WebSocket,
|
||||
new
|
||||
{
|
||||
method = "UNSUBSCRIBE",
|
||||
@params = new[]
|
||||
{
|
||||
$"{symbol.Value.ToLowerInvariant()}@trade",
|
||||
$"{symbol.Value.ToLowerInvariant()}@bookTicker"
|
||||
},
|
||||
id = GetNextRequestId()
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Send(IWebSocket webSocket, object obj)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(obj);
|
||||
|
||||
if (!_webSocketRateLimiter.WaitToProceed(TimeSpan.Zero))
|
||||
{
|
||||
_webSocketRateLimiter.WaitToProceed();
|
||||
}
|
||||
|
||||
Log.Trace("Send: " + json);
|
||||
|
||||
webSocket.Send(json);
|
||||
}
|
||||
|
||||
private long GetNextRequestId()
|
||||
{
|
||||
return Interlocked.Increment(ref _lastRequestId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event invocator for the OrderFilled event
|
||||
/// </summary>
|
||||
/// <param name="e">The OrderEvent</param>
|
||||
private void OnOrderSubmit(BinanceOrderSubmitEventArgs e)
|
||||
{
|
||||
var brokerId = e.BrokerId;
|
||||
var order = e.Order;
|
||||
if (CachedOrderIDs.ContainsKey(order.Id))
|
||||
{
|
||||
CachedOrderIDs[order.Id].BrokerId.Clear();
|
||||
CachedOrderIDs[order.Id].BrokerId.Add(brokerId);
|
||||
}
|
||||
else
|
||||
{
|
||||
order.BrokerId.Add(brokerId);
|
||||
CachedOrderIDs.TryAdd(order.Id, order);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Brokerages/Binance/BinanceBrokerageFactory.cs
Normal file
88
Brokerages/Binance/BinanceBrokerageFactory.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.Securities;
|
||||
using QuantConnect.Util;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory method to create binance Websockets brokerage
|
||||
/// </summary>
|
||||
public class BinanceBrokerageFactory : BrokerageFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory constructor
|
||||
/// </summary>
|
||||
public BinanceBrokerageFactory() : base(typeof(BinanceBrokerage))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not required
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// provides brokerage connection data
|
||||
/// </summary>
|
||||
public override Dictionary<string, string> BrokerageData => new Dictionary<string, string>
|
||||
{
|
||||
{ "binance-api-key", Config.Get("binance-api-key")},
|
||||
{ "binance-api-secret", Config.Get("binance-api-secret")}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The brokerage model
|
||||
/// </summary>
|
||||
/// <param name="orderProvider">The order provider</param>
|
||||
public override IBrokerageModel GetBrokerageModel(IOrderProvider orderProvider) => new BinanceBrokerageModel();
|
||||
|
||||
/// <summary>
|
||||
/// Create the Brokerage instance
|
||||
/// </summary>
|
||||
/// <param name="job"></param>
|
||||
/// <param name="algorithm"></param>
|
||||
/// <returns></returns>
|
||||
public override IBrokerage CreateBrokerage(Packets.LiveNodePacket job, IAlgorithm algorithm)
|
||||
{
|
||||
var required = new[] { "binance-api-secret", "binance-api-key" };
|
||||
|
||||
foreach (var item in required)
|
||||
{
|
||||
if (string.IsNullOrEmpty(job.BrokerageData[item]))
|
||||
{
|
||||
throw new Exception($"BinanceBrokerageFactory.CreateBrokerage: Missing {item} in config.json");
|
||||
}
|
||||
}
|
||||
|
||||
var brokerage = new BinanceBrokerage(
|
||||
job.BrokerageData["binance-api-key"],
|
||||
job.BrokerageData["binance-api-secret"],
|
||||
algorithm,
|
||||
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager")));
|
||||
Composer.Instance.AddPart<IDataQueueHandler>(brokerage);
|
||||
|
||||
return brokerage;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Brokerages/Binance/BinanceOrderSubmitEventArgs.cs
Normal file
46
Brokerages/Binance/BinanceOrderSubmitEventArgs.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.Orders;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a binance submit order event data
|
||||
/// </summary>
|
||||
public class BinanceOrderSubmitEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Order Event Constructor.
|
||||
/// </summary>
|
||||
/// <param name="brokerId">Binance order id returned from brokerage</param>
|
||||
/// <param name="order">Order for this order placement</param>
|
||||
public BinanceOrderSubmitEventArgs(string brokerId, Order order)
|
||||
{
|
||||
BrokerId = brokerId;
|
||||
Order = order;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Original brokerage id
|
||||
/// </summary>
|
||||
public string BrokerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The lean order
|
||||
/// </summary>
|
||||
public Order Order { get; set; }
|
||||
}
|
||||
}
|
||||
590
Brokerages/Binance/BinanceRestApiClient.cs
Normal file
590
Brokerages/Binance/BinanceRestApiClient.cs
Normal file
@@ -0,0 +1,590 @@
|
||||
/*
|
||||
* 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 Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Orders.Fees;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Util;
|
||||
using RestSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Binance REST API implementation
|
||||
/// </summary>
|
||||
public class BinanceRestApiClient : IDisposable
|
||||
{
|
||||
private const string RestApiUrl = "https://api.binance.com";
|
||||
private const string UserDataStreamEndpoint = "/api/v3/userDataStream";
|
||||
|
||||
private readonly BinanceSymbolMapper _symbolMapper;
|
||||
private readonly ISecurityProvider _securityProvider;
|
||||
private readonly IRestClient _restClient;
|
||||
private readonly RateGate _restRateLimiter = new RateGate(10, TimeSpan.FromSeconds(1));
|
||||
private readonly object _listenKeyLocker = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Event that fires each time an order is filled
|
||||
/// </summary>
|
||||
public event EventHandler<BinanceOrderSubmitEventArgs> OrderSubmit;
|
||||
|
||||
/// <summary>
|
||||
/// Event that fires each time an order is filled
|
||||
/// </summary>
|
||||
public event EventHandler<OrderEvent> OrderStatusChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event that fires when an error is encountered in the brokerage
|
||||
/// </summary>
|
||||
public event EventHandler<BrokerageMessageEvent> Message;
|
||||
|
||||
/// <summary>
|
||||
/// Key Header
|
||||
/// </summary>
|
||||
public readonly string KeyHeader = "X-MBX-APIKEY";
|
||||
|
||||
/// <summary>
|
||||
/// The api secret
|
||||
/// </summary>
|
||||
protected string ApiSecret;
|
||||
|
||||
/// <summary>
|
||||
/// The api key
|
||||
/// </summary>
|
||||
protected string ApiKey;
|
||||
|
||||
/// <summary>
|
||||
/// Represents UserData Session listen key
|
||||
/// </summary>
|
||||
public string SessionId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BinanceRestApiClient"/> class.
|
||||
/// </summary>
|
||||
/// <param name="symbolMapper">The symbol mapper.</param>
|
||||
/// <param name="securityProvider">The holdings provider.</param>
|
||||
/// <param name="apiKey">The Binance API key</param>
|
||||
/// <param name="apiSecret">The The Binance API secret</param>
|
||||
public BinanceRestApiClient(BinanceSymbolMapper symbolMapper, ISecurityProvider securityProvider, string apiKey, string apiSecret)
|
||||
{
|
||||
_symbolMapper = symbolMapper;
|
||||
_securityProvider = securityProvider;
|
||||
_restClient = new RestClient(RestApiUrl);
|
||||
ApiKey = apiKey;
|
||||
ApiSecret = apiSecret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all open positions
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<Holding> GetAccountHoldings()
|
||||
{
|
||||
return new List<Holding>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total account cash balance for specified account type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Messages.AccountInformation GetCashBalance()
|
||||
{
|
||||
var queryString = $"timestamp={GetNonce()}";
|
||||
var endpoint = $"/api/v3/account?{queryString}&signature={AuthenticationToken(queryString)}";
|
||||
var request = new RestRequest(endpoint, Method.GET);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
|
||||
var response = ExecuteRestRequest(request);
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception($"BinanceBrokerage.GetCashBalance: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject<Messages.AccountInformation>(response.Content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all orders not yet closed
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<Messages.OpenOrder> GetOpenOrders()
|
||||
{
|
||||
var queryString = $"timestamp={GetNonce()}";
|
||||
var endpoint = $"/api/v3/openOrders?{queryString}&signature={AuthenticationToken(queryString)}";
|
||||
var request = new RestRequest(endpoint, Method.GET);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
|
||||
var response = ExecuteRestRequest(request);
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception($"BinanceBrokerage.GetCashBalance: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject<Messages.OpenOrder[]>(response.Content);
|
||||
}
|
||||
|
||||
/// <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 bool PlaceOrder(Order order)
|
||||
{
|
||||
// supported time in force values {GTC, IOC, FOK}
|
||||
// use GTC as LEAN doesn't support others yet
|
||||
IDictionary<string, object> body = new Dictionary<string, object>()
|
||||
{
|
||||
{ "symbol", _symbolMapper.GetBrokerageSymbol(order.Symbol) },
|
||||
{ "quantity", Math.Abs(order.Quantity).ToString(CultureInfo.InvariantCulture) },
|
||||
{ "side", ConvertOrderDirection(order.Direction) }
|
||||
};
|
||||
|
||||
switch (order.Type)
|
||||
{
|
||||
case OrderType.Limit:
|
||||
body["type"] = (order.Properties as BinanceOrderProperties)?.PostOnly == true
|
||||
? "LIMIT_MAKER"
|
||||
: "LIMIT";
|
||||
body["price"] = ((LimitOrder) order).LimitPrice.ToString(CultureInfo.InvariantCulture);
|
||||
// timeInForce is not required for LIMIT_MAKER
|
||||
if (Equals(body["type"], "LIMIT"))
|
||||
body["timeInForce"] = "GTC";
|
||||
break;
|
||||
case OrderType.Market:
|
||||
body["type"] = "MARKET";
|
||||
break;
|
||||
case OrderType.StopLimit:
|
||||
var ticker = GetTickerPrice(order);
|
||||
var stopPrice = ((StopLimitOrder) order).StopPrice;
|
||||
if (order.Direction == OrderDirection.Sell)
|
||||
{
|
||||
body["type"] = stopPrice <= ticker ? "STOP_LOSS_LIMIT" : "TAKE_PROFIT_LIMIT";
|
||||
}
|
||||
else
|
||||
{
|
||||
body["type"] = stopPrice <= ticker ? "TAKE_PROFIT_LIMIT" : "STOP_LOSS_LIMIT";
|
||||
}
|
||||
body["timeInForce"] = "GTC";
|
||||
body["stopPrice"] = stopPrice.ToString(CultureInfo.InvariantCulture);
|
||||
body["price"] = ((StopLimitOrder) order).LimitPrice.ToString(CultureInfo.InvariantCulture);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"BinanceBrokerage.ConvertOrderType: Unsupported order type: {order.Type}");
|
||||
}
|
||||
|
||||
const string endpoint = "/api/v3/order";
|
||||
body["timestamp"] = GetNonce();
|
||||
body["signature"] = AuthenticationToken(body.ToQueryString());
|
||||
var request = new RestRequest(endpoint, Method.POST);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
request.AddParameter(
|
||||
"application/x-www-form-urlencoded",
|
||||
Encoding.UTF8.GetBytes(body.ToQueryString()),
|
||||
ParameterType.RequestBody
|
||||
);
|
||||
|
||||
var response = ExecuteRestRequest(request);
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
var raw = JsonConvert.DeserializeObject<Messages.NewOrder>(response.Content);
|
||||
|
||||
if (string.IsNullOrEmpty(raw?.Id))
|
||||
{
|
||||
var errorMessage = $"Error parsing response from place order: {response.Content}";
|
||||
OnOrderEvent(new OrderEvent(
|
||||
order,
|
||||
DateTime.UtcNow,
|
||||
OrderFee.Zero,
|
||||
"Binance Order Event")
|
||||
{ Status = OrderStatus.Invalid, Message = errorMessage });
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, (int)response.StatusCode, errorMessage));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
OnOrderSubmit(raw, order);
|
||||
return true;
|
||||
}
|
||||
|
||||
var message = $"Order failed, Order Id: {order.Id} timestamp: {order.Time} quantity: {order.Quantity} content: {response.Content}";
|
||||
OnOrderEvent(new OrderEvent(
|
||||
order,
|
||||
DateTime.UtcNow,
|
||||
OrderFee.Zero,
|
||||
"Binance Order Event")
|
||||
{ Status = OrderStatus.Invalid });
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, -1, message));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the order with the specified ID
|
||||
/// </summary>
|
||||
/// <param name="order">The order to cancel</param>
|
||||
/// <returns>True if the request was submitted for cancellation, false otherwise</returns>
|
||||
public bool CancelOrder(Order order)
|
||||
{
|
||||
var success = new List<bool>();
|
||||
IDictionary<string, object> body = new Dictionary<string, object>()
|
||||
{
|
||||
{ "symbol", _symbolMapper.GetBrokerageSymbol(order.Symbol) }
|
||||
};
|
||||
foreach (var id in order.BrokerId)
|
||||
{
|
||||
if (body.ContainsKey("signature"))
|
||||
{
|
||||
body.Remove("signature");
|
||||
}
|
||||
body["orderId"] = id;
|
||||
body["timestamp"] = GetNonce();
|
||||
body["signature"] = AuthenticationToken(body.ToQueryString());
|
||||
|
||||
var request = new RestRequest("/api/v3/order", Method.DELETE);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
request.AddParameter(
|
||||
"application/x-www-form-urlencoded",
|
||||
Encoding.UTF8.GetBytes(body.ToQueryString()),
|
||||
ParameterType.RequestBody
|
||||
);
|
||||
|
||||
var response = ExecuteRestRequest(request);
|
||||
success.Add(response.StatusCode == HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
var canceled = false;
|
||||
if (success.All(a => a))
|
||||
{
|
||||
OnOrderEvent(new OrderEvent(order,
|
||||
DateTime.UtcNow,
|
||||
OrderFee.Zero,
|
||||
"Binance Order Event")
|
||||
{ Status = OrderStatus.Canceled });
|
||||
|
||||
canceled = true;
|
||||
}
|
||||
return canceled;
|
||||
}
|
||||
|
||||
/// <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 IEnumerable<Messages.Kline> GetHistory(Data.HistoryRequest request)
|
||||
{
|
||||
var resolution = ConvertResolution(request.Resolution);
|
||||
var resolutionInMs = (long)request.Resolution.ToTimeSpan().TotalMilliseconds;
|
||||
var symbol = _symbolMapper.GetBrokerageSymbol(request.Symbol);
|
||||
var startMs = (long)Time.DateTimeToUnixTimeStamp(request.StartTimeUtc) * 1000;
|
||||
var endMs = (long)Time.DateTimeToUnixTimeStamp(request.EndTimeUtc) * 1000;
|
||||
var endpoint = $"/api/v3/klines?symbol={symbol}&interval={resolution}&limit=1000";
|
||||
|
||||
while (endMs - startMs >= resolutionInMs)
|
||||
{
|
||||
var timeframe = $"&startTime={startMs}&endTime={endMs}";
|
||||
|
||||
var restRequest = new RestRequest(endpoint + timeframe, Method.GET);
|
||||
var response = ExecuteRestRequest(restRequest);
|
||||
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception($"BinanceBrokerage.GetHistory: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
|
||||
}
|
||||
|
||||
var klines = JsonConvert.DeserializeObject<object[][]>(response.Content)
|
||||
.Select(entries => new Messages.Kline(entries))
|
||||
.ToList();
|
||||
|
||||
startMs = klines.Last().OpenTime + resolutionInMs;
|
||||
|
||||
foreach (var kline in klines)
|
||||
{
|
||||
yield return kline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check User Data stream listen key is alive
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool SessionKeepAlive()
|
||||
{
|
||||
if (string.IsNullOrEmpty(SessionId))
|
||||
{
|
||||
throw new Exception("BinanceBrokerage:UserStream. listenKey wasn't allocated or has been refused.");
|
||||
}
|
||||
|
||||
var ping = new RestRequest(UserDataStreamEndpoint, Method.PUT);
|
||||
ping.AddHeader(KeyHeader, ApiKey);
|
||||
ping.AddParameter(
|
||||
"application/x-www-form-urlencoded",
|
||||
Encoding.UTF8.GetBytes($"listenKey={SessionId}"),
|
||||
ParameterType.RequestBody
|
||||
);
|
||||
|
||||
var pong = ExecuteRestRequest(ping);
|
||||
return pong.StatusCode == HttpStatusCode.OK;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the session
|
||||
/// </summary>
|
||||
public void StopSession()
|
||||
{
|
||||
if (string.IsNullOrEmpty(SessionId))
|
||||
{
|
||||
throw new Exception("BinanceBrokerage:UserStream. listenKey wasn't allocated or has been refused.");
|
||||
}
|
||||
|
||||
var request = new RestRequest(UserDataStreamEndpoint, Method.DELETE);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
request.AddParameter(
|
||||
"application/x-www-form-urlencoded",
|
||||
Encoding.UTF8.GetBytes($"listenKey={SessionId}"),
|
||||
ParameterType.RequestBody
|
||||
);
|
||||
ExecuteRestRequest(request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides the current tickers price
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Messages.PriceTicker[] GetTickers()
|
||||
{
|
||||
const string endpoint = "/api/v3/ticker/price";
|
||||
var req = new RestRequest(endpoint, Method.GET);
|
||||
var response = ExecuteRestRequest(req);
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception($"BinanceBrokerage.GetTick: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject<Messages.PriceTicker[]>(response.Content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start user data stream
|
||||
/// </summary>
|
||||
public void CreateListenKey()
|
||||
{
|
||||
var request = new RestRequest(UserDataStreamEndpoint, Method.POST);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
|
||||
var response = ExecuteRestRequest(request);
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception($"BinanceBrokerage.StartSession: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
|
||||
}
|
||||
|
||||
var content = JObject.Parse(response.Content);
|
||||
lock (_listenKeyLocker)
|
||||
{
|
||||
SessionId = content.Value<string>("listenKey");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_restRateLimiter.DisposeSafely();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If an IP address exceeds a certain number of requests per minute
|
||||
/// HTTP 429 return code is used when breaking a request rate limit.
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
private IRestResponse ExecuteRestRequest(IRestRequest request)
|
||||
{
|
||||
const int maxAttempts = 10;
|
||||
var attempts = 0;
|
||||
IRestResponse response;
|
||||
|
||||
do
|
||||
{
|
||||
if (!_restRateLimiter.WaitToProceed(TimeSpan.Zero))
|
||||
{
|
||||
Log.Trace("Brokerage.OnMessage(): " + new BrokerageMessageEvent(BrokerageMessageType.Warning, "RateLimit",
|
||||
"The API request has been rate limited. To avoid this message, please reduce the frequency of API calls."));
|
||||
|
||||
_restRateLimiter.WaitToProceed();
|
||||
}
|
||||
|
||||
response = _restClient.Execute(request);
|
||||
// 429 status code: Too Many Requests
|
||||
} while (++attempts < maxAttempts && (int)response.StatusCode == 429);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private decimal GetTickerPrice(Order order)
|
||||
{
|
||||
var security = _securityProvider.GetSecurity(order.Symbol);
|
||||
var tickerPrice = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
|
||||
if (tickerPrice == 0)
|
||||
{
|
||||
var brokerageSymbol = _symbolMapper.GetBrokerageSymbol(order.Symbol);
|
||||
var tickers = GetTickers();
|
||||
var ticker = tickers.FirstOrDefault(t => t.Symbol == brokerageSymbol);
|
||||
if (ticker == null)
|
||||
{
|
||||
throw new KeyNotFoundException($"BinanceBrokerage: Unable to resolve currency conversion pair: {order.Symbol}");
|
||||
}
|
||||
tickerPrice = ticker.Price;
|
||||
}
|
||||
return tickerPrice;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp in milliseconds
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private long GetNonce()
|
||||
{
|
||||
return (long)(Time.TimeStamp() * 1000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a signature for signed endpoints
|
||||
/// </summary>
|
||||
/// <param name="payload">the body of the request</param>
|
||||
/// <returns>a token representing the request params</returns>
|
||||
private string AuthenticationToken(string payload)
|
||||
{
|
||||
using (HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(ApiSecret)))
|
||||
{
|
||||
return hmac.ComputeHash(Encoding.UTF8.GetBytes(payload)).ToHexString();
|
||||
}
|
||||
}
|
||||
|
||||
private static string ConvertOrderDirection(OrderDirection orderDirection)
|
||||
{
|
||||
if (orderDirection == OrderDirection.Buy || orderDirection == OrderDirection.Sell)
|
||||
{
|
||||
return orderDirection.ToString().LazyToUpper();
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"BinanceBrokerage.ConvertOrderDirection: Unsupported order direction: {orderDirection}");
|
||||
}
|
||||
|
||||
|
||||
private readonly Dictionary<Resolution, string> _knownResolutions = new Dictionary<Resolution, string>()
|
||||
{
|
||||
{ Resolution.Minute, "1m" },
|
||||
{ Resolution.Hour, "1h" },
|
||||
{ Resolution.Daily, "1d" }
|
||||
};
|
||||
|
||||
private string ConvertResolution(Resolution resolution)
|
||||
{
|
||||
if (_knownResolutions.ContainsKey(resolution))
|
||||
{
|
||||
return _knownResolutions[resolution];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"BinanceBrokerage.ConvertResolution: Unsupported resolution type: {resolution}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event invocator for the OrderFilled event
|
||||
/// </summary>
|
||||
/// <param name="newOrder">The brokerage order submit result</param>
|
||||
/// <param name="order">The lean order</param>
|
||||
private void OnOrderSubmit(Messages.NewOrder newOrder, Order order)
|
||||
{
|
||||
try
|
||||
{
|
||||
OrderSubmit?.Invoke(
|
||||
this,
|
||||
new BinanceOrderSubmitEventArgs(newOrder.Id, order));
|
||||
|
||||
// Generate submitted event
|
||||
OnOrderEvent(new OrderEvent(
|
||||
order,
|
||||
Time.UnixMillisecondTimeStampToDateTime(newOrder.TransactionTime),
|
||||
OrderFee.Zero,
|
||||
"Binance Order Event")
|
||||
{ Status = OrderStatus.Submitted }
|
||||
);
|
||||
Log.Trace($"Order submitted successfully - OrderId: {order.Id}");
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
Log.Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event invocator for the OrderFilled event
|
||||
/// </summary>
|
||||
/// <param name="e">The OrderEvent</param>
|
||||
private void OnOrderEvent(OrderEvent e)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.Debug("Brokerage.OnOrderEvent(): " + e);
|
||||
|
||||
OrderStatusChanged?.Invoke(this, e);
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
Log.Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event invocator for the Message event
|
||||
/// </summary>
|
||||
/// <param name="e">The error</param>
|
||||
protected virtual void OnMessage(BrokerageMessageEvent e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.Type == BrokerageMessageType.Error)
|
||||
{
|
||||
Log.Error("Brokerage.OnMessage(): " + e);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Trace("Brokerage.OnMessage(): " + e);
|
||||
}
|
||||
|
||||
Message?.Invoke(this, e);
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
Log.Error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
174
Brokerages/Binance/BinanceSymbolMapper.cs
Normal file
174
Brokerages/Binance/BinanceSymbolMapper.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.Securities;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the mapping between Lean symbols and Binance symbols.
|
||||
/// </summary>
|
||||
public class BinanceSymbolMapper : ISymbolMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of known Binance symbols.
|
||||
/// </summary>
|
||||
public static readonly HashSet<string> KnownTickers =
|
||||
new HashSet<string>(SymbolPropertiesDatabase
|
||||
.FromDataFolder()
|
||||
.GetSymbolPropertiesList(Market.Binance, SecurityType.Crypto)
|
||||
.Select(x => x.Key.Symbol));
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Lean symbol instance to an Binance symbol
|
||||
/// </summary>
|
||||
/// <param name="symbol">A Lean symbol instance</param>
|
||||
/// <returns>The Binance 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.SecurityType != SecurityType.Crypto)
|
||||
throw new ArgumentException("Invalid security type: " + symbol.SecurityType);
|
||||
|
||||
var brokerageSymbol = ConvertLeanSymbolToBrokerageSymbol(symbol.Value);
|
||||
|
||||
if (!IsKnownBrokerageSymbol(brokerageSymbol))
|
||||
throw new ArgumentException("Unknown symbol: " + symbol.Value);
|
||||
|
||||
return brokerageSymbol;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an Binance symbol to a Lean symbol instance
|
||||
/// </summary>
|
||||
/// <param name="brokerageSymbol">The Binance 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 Binance symbol: {brokerageSymbol}");
|
||||
|
||||
if (!IsKnownBrokerageSymbol(brokerageSymbol))
|
||||
throw new ArgumentException($"Unknown Binance symbol: {brokerageSymbol}");
|
||||
|
||||
if (securityType != SecurityType.Crypto)
|
||||
throw new ArgumentException($"Invalid security type: {securityType}");
|
||||
|
||||
if (market != Market.Binance)
|
||||
throw new ArgumentException($"Invalid market: {market}");
|
||||
|
||||
return Symbol.Create(ConvertBrokerageSymbolToLeanSymbol(brokerageSymbol), GetBrokerageSecurityType(brokerageSymbol), Market.Binance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an Binance symbol to a Lean symbol instance
|
||||
/// </summary>
|
||||
/// <param name="brokerageSymbol">The Binance symbol</param>
|
||||
/// <returns>A new Lean Symbol instance</returns>
|
||||
public Symbol GetLeanSymbol(string brokerageSymbol)
|
||||
{
|
||||
var securityType = GetBrokerageSecurityType(brokerageSymbol);
|
||||
return GetLeanSymbol(brokerageSymbol, securityType, Market.Binance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the security type for an Binance symbol
|
||||
/// </summary>
|
||||
/// <param name="brokerageSymbol">The Binance symbol</param>
|
||||
/// <returns>The security type</returns>
|
||||
public SecurityType GetBrokerageSecurityType(string brokerageSymbol)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(brokerageSymbol))
|
||||
throw new ArgumentException($"Invalid Binance symbol: {brokerageSymbol}");
|
||||
|
||||
if (!IsKnownBrokerageSymbol(brokerageSymbol))
|
||||
throw new ArgumentException($"Unknown Binance symbol: {brokerageSymbol}");
|
||||
|
||||
return SecurityType.Crypto;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
return GetBrokerageSecurityType(ConvertLeanSymbolToBrokerageSymbol(leanSymbol));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the symbol is supported by Binance
|
||||
/// </summary>
|
||||
/// <param name="brokerageSymbol">The Binance symbol</param>
|
||||
/// <returns>True if Binance supports the symbol</returns>
|
||||
public bool IsKnownBrokerageSymbol(string brokerageSymbol)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(brokerageSymbol))
|
||||
return false;
|
||||
|
||||
return KnownTickers.Contains(brokerageSymbol);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the symbol is supported by Binance
|
||||
/// </summary>
|
||||
/// <param name="symbol">The Lean symbol</param>
|
||||
/// <returns>True if Binance supports the symbol</returns>
|
||||
public bool IsKnownLeanSymbol(Symbol symbol)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(symbol?.Value) || symbol.Value.Length <= 3)
|
||||
return false;
|
||||
|
||||
var binanceSymbol = ConvertLeanSymbolToBrokerageSymbol(symbol.Value);
|
||||
|
||||
return IsKnownBrokerageSymbol(binanceSymbol) && GetBrokerageSecurityType(binanceSymbol) == symbol.SecurityType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an Binance symbol to a Lean symbol string
|
||||
/// </summary>
|
||||
private static string ConvertBrokerageSymbolToLeanSymbol(string binanceSymbol)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(binanceSymbol))
|
||||
throw new ArgumentException($"Invalid Binance symbol: {binanceSymbol}");
|
||||
|
||||
// return as it is due to Binance has similar Symbol format
|
||||
return binanceSymbol.LazyToUpper();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Lean symbol string to an Binance symbol
|
||||
/// </summary>
|
||||
private static string ConvertLeanSymbolToBrokerageSymbol(string leanSymbol)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(leanSymbol))
|
||||
throw new ArgumentException($"Invalid Lean symbol: {leanSymbol}");
|
||||
|
||||
// return as it is due to Binance has similar Symbol format
|
||||
return leanSymbol.LazyToUpper();
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Brokerages/Binance/BinanceUtil.cs
Normal file
60
Brokerages/Binance/BinanceUtil.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 Newtonsoft.Json;
|
||||
using QuantConnect.Orders;
|
||||
using RestSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Binance utility methods
|
||||
/// </summary>
|
||||
public class BinanceUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert binance status string value to native Lean OrderStatus
|
||||
/// </summary>
|
||||
/// <param name="status">The Binance order status value</param>
|
||||
/// <returns>Lean order status</returns>
|
||||
public static OrderStatus ConvertOrderStatus(string status)
|
||||
{
|
||||
switch (status.LazyToUpper())
|
||||
{
|
||||
case "NEW":
|
||||
return OrderStatus.New;
|
||||
case "PARTIALLY_FILLED":
|
||||
return OrderStatus.PartiallyFilled;
|
||||
case "FILLED":
|
||||
return OrderStatus.Filled;
|
||||
case "PENDING_CANCEL":
|
||||
return OrderStatus.CancelPending;
|
||||
case "CANCELED":
|
||||
return OrderStatus.Canceled;
|
||||
case "REJECTED":
|
||||
case "EXPIRED":
|
||||
return OrderStatus.Invalid;
|
||||
default:
|
||||
return Orders.OrderStatus.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
Brokerages/Binance/BinanceWebSocketWrapper.cs
Normal file
44
Brokerages/Binance/BinanceWebSocketWrapper.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper class for a Binance websocket connection
|
||||
/// </summary>
|
||||
public class BinanceWebSocketWrapper : WebSocketClientWrapper
|
||||
{
|
||||
/// <summary>
|
||||
/// The unique Id for the connection
|
||||
/// </summary>
|
||||
public string ConnectionId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The handler for the connection
|
||||
/// </summary>
|
||||
public IConnectionHandler ConnectionHandler { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BinanceWebSocketWrapper"/> class.
|
||||
/// </summary>
|
||||
public BinanceWebSocketWrapper(IConnectionHandler connectionHandler)
|
||||
{
|
||||
ConnectionId = Guid.NewGuid().ToString();
|
||||
ConnectionHandler = connectionHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
209
Brokerages/Binance/Messages.cs
Normal file
209
Brokerages/Binance/Messages.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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 Newtonsoft.Json;
|
||||
using QuantConnect.Orders;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance.Messages
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
|
||||
public class AccountInformation
|
||||
{
|
||||
public Balance[] Balances { get; set; }
|
||||
|
||||
public class Balance
|
||||
{
|
||||
public string Asset { get; set; }
|
||||
public decimal Free { get; set; }
|
||||
public decimal Locked { get; set; }
|
||||
public decimal Amount => Free + Locked;
|
||||
}
|
||||
}
|
||||
|
||||
public class PriceTicker
|
||||
{
|
||||
public string Symbol { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
}
|
||||
|
||||
public class Order
|
||||
{
|
||||
[JsonProperty("orderId")]
|
||||
public string Id { get; set; }
|
||||
public string Symbol { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public decimal StopPrice { get; set; }
|
||||
[JsonProperty("origQty")]
|
||||
public decimal OriginalAmount { get; set; }
|
||||
[JsonProperty("executedQty")]
|
||||
public decimal ExecutedAmount { get; set; }
|
||||
public string Status { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Side { get; set; }
|
||||
|
||||
public decimal Quantity => string.Equals(Side, "buy", StringComparison.OrdinalIgnoreCase) ? OriginalAmount : -OriginalAmount;
|
||||
}
|
||||
|
||||
public class OpenOrder : Order
|
||||
{
|
||||
public long Time { get; set; }
|
||||
}
|
||||
|
||||
public class NewOrder : Order
|
||||
{
|
||||
[JsonProperty("transactTime")]
|
||||
public long TransactionTime { get; set; }
|
||||
}
|
||||
|
||||
public enum EventType
|
||||
{
|
||||
None,
|
||||
OrderBook,
|
||||
Trade,
|
||||
Execution
|
||||
}
|
||||
|
||||
public class ErrorMessage
|
||||
{
|
||||
[JsonProperty("code")]
|
||||
public int Code { get; set; }
|
||||
|
||||
[JsonProperty("msg")]
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
public class BestBidAskQuote
|
||||
{
|
||||
[JsonProperty("u")]
|
||||
public long OrderBookUpdateId { get; set; }
|
||||
|
||||
[JsonProperty("s")]
|
||||
public string Symbol { get; set; }
|
||||
|
||||
[JsonProperty("b")]
|
||||
public decimal BestBidPrice { get; set; }
|
||||
|
||||
[JsonProperty("B")]
|
||||
public decimal BestBidSize { get; set; }
|
||||
|
||||
[JsonProperty("a")]
|
||||
public decimal BestAskPrice { get; set; }
|
||||
|
||||
[JsonProperty("A")]
|
||||
public decimal BestAskSize { get; set; }
|
||||
}
|
||||
|
||||
public class BaseMessage
|
||||
{
|
||||
public virtual EventType @Event { get; } = EventType.None;
|
||||
|
||||
[JsonProperty("e")]
|
||||
public string EventName { get; set; }
|
||||
|
||||
[JsonProperty("E")]
|
||||
public long Time { get; set; }
|
||||
|
||||
[JsonProperty("s")]
|
||||
public string Symbol { get; set; }
|
||||
}
|
||||
|
||||
public class Trade : BaseMessage
|
||||
{
|
||||
public override EventType @Event => EventType.Trade;
|
||||
|
||||
[JsonProperty("T")]
|
||||
public new long Time { get; set; }
|
||||
|
||||
[JsonProperty("p")]
|
||||
public decimal Price { get; private set; }
|
||||
|
||||
[JsonProperty("q")]
|
||||
public decimal Quantity { get; private set; }
|
||||
}
|
||||
|
||||
public class Execution : BaseMessage
|
||||
{
|
||||
public override EventType @Event => EventType.Execution;
|
||||
|
||||
[JsonProperty("i")]
|
||||
public string OrderId { get; set; }
|
||||
|
||||
[JsonProperty("t")]
|
||||
public string TradeId { get; set; }
|
||||
|
||||
[JsonProperty("I")]
|
||||
public string Ignore { get; set; }
|
||||
|
||||
[JsonProperty("x")]
|
||||
public string ExecutionType { get; private set; }
|
||||
|
||||
[JsonProperty("X")]
|
||||
public string OrderStatus { get; private set; }
|
||||
|
||||
[JsonProperty("T")]
|
||||
public long TransactionTime { get; set; }
|
||||
|
||||
[JsonProperty("L")]
|
||||
public decimal LastExecutedPrice { get; set; }
|
||||
|
||||
[JsonProperty("l")]
|
||||
public decimal LastExecutedQuantity { get; set; }
|
||||
|
||||
[JsonProperty("S")]
|
||||
public string Side { get; set; }
|
||||
|
||||
[JsonProperty("n")]
|
||||
public decimal Fee { get; set; }
|
||||
|
||||
[JsonProperty("N")]
|
||||
public string FeeCurrency { get; set; }
|
||||
|
||||
public OrderDirection Direction => Side.Equals("BUY", StringComparison.OrdinalIgnoreCase) ? OrderDirection.Buy : OrderDirection.Sell;
|
||||
}
|
||||
|
||||
public class Kline
|
||||
{
|
||||
public long OpenTime { get; }
|
||||
public decimal Open { get; }
|
||||
public decimal Close { get; }
|
||||
public decimal High { get; }
|
||||
public decimal Low { get; }
|
||||
public decimal Volume { get; }
|
||||
|
||||
public Kline() { }
|
||||
|
||||
public Kline(long msts, decimal close)
|
||||
{
|
||||
OpenTime = msts;
|
||||
Open = Close = High = Low = close;
|
||||
Volume = 0;
|
||||
}
|
||||
|
||||
public Kline(object[] entries)
|
||||
{
|
||||
OpenTime = Convert.ToInt64(entries[0], CultureInfo.InvariantCulture);
|
||||
Open = ((string)entries[1]).ToDecimal();
|
||||
High = ((string)entries[2]).ToDecimal();
|
||||
Low = ((string)entries[3]).ToDecimal();
|
||||
Close = ((string)entries[4]).ToDecimal();
|
||||
Volume = ((string)entries[5]).ToDecimal();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -399,6 +399,15 @@ namespace QuantConnect.Brokerages.Bitfinex
|
||||
: OrderStatus.PartiallyFilled;
|
||||
}
|
||||
|
||||
if (_algorithm.BrokerageModel.AccountType == AccountType.Cash &&
|
||||
order.Direction == OrderDirection.Buy)
|
||||
{
|
||||
// fees are debited in the base currency, so we have to subtract them from the filled quantity
|
||||
fillQuantity -= orderFee.Value.Amount;
|
||||
|
||||
orderFee = new ModifiedFillQuantityOrderFee(orderFee.Value);
|
||||
}
|
||||
|
||||
var orderEvent = new OrderEvent
|
||||
(
|
||||
order.Id, symbol, updTime, status,
|
||||
|
||||
@@ -223,6 +223,11 @@ namespace QuantConnect.Brokerages
|
||||
/// </summary>
|
||||
public virtual bool AccountInstantlyUpdated => false;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the brokerage account's base currency
|
||||
/// </summary>
|
||||
public virtual string AccountBaseCurrency { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the history for the requested security
|
||||
/// </summary>
|
||||
|
||||
@@ -59,8 +59,6 @@ namespace QuantConnect.Brokerages.Fxcm
|
||||
private readonly Dictionary<string, AutoResetEvent> _mapRequestsToAutoResetEvents = new Dictionary<string, AutoResetEvent>();
|
||||
private readonly HashSet<string> _pendingHistoryRequests = new HashSet<string>();
|
||||
|
||||
private string _fxcmAccountCurrency = Currencies.USD;
|
||||
|
||||
private void LoadInstruments()
|
||||
{
|
||||
// Note: requestTradingSessionStatus() MUST be called just after login
|
||||
@@ -246,7 +244,7 @@ namespace QuantConnect.Brokerages.Fxcm
|
||||
}
|
||||
|
||||
// get account base currency
|
||||
_fxcmAccountCurrency = message.getParameter("BASE_CRNCY").getValue();
|
||||
AccountBaseCurrency = message.getParameter("BASE_CRNCY").getValue();
|
||||
|
||||
_mapRequestsToAutoResetEvents[_currentRequest].Set();
|
||||
_mapRequestsToAutoResetEvents.Remove(_currentRequest);
|
||||
|
||||
@@ -431,7 +431,7 @@ namespace QuantConnect.Brokerages.Fxcm
|
||||
|
||||
//Adds the account currency to the cashbook.
|
||||
cashBook.Add(new CashAmount(Convert.ToDecimal(_accounts[_accountId].getCashOutstanding()),
|
||||
_fxcmAccountCurrency));
|
||||
AccountBaseCurrency));
|
||||
|
||||
// include cash balances from currency swaps for open Forex positions
|
||||
foreach (var trade in _openPositions.Values)
|
||||
|
||||
@@ -35,6 +35,14 @@ namespace QuantConnect.Brokerages.GDAX
|
||||
{
|
||||
private const int MaxDataPointsPerHistoricalRequest = 300;
|
||||
|
||||
// These are the only currencies accepted for fiat deposits
|
||||
private static readonly HashSet<string> FiatCurrencies = new List<string>
|
||||
{
|
||||
Currencies.EUR,
|
||||
Currencies.GBP,
|
||||
Currencies.USD
|
||||
}.ToHashSet();
|
||||
|
||||
#region IBrokerage
|
||||
/// <summary>
|
||||
/// Checks if the websocket connection is connected or in the process of connecting
|
||||
@@ -186,6 +194,16 @@ namespace QuantConnect.Brokerages.GDAX
|
||||
return success.All(a => a);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connects the client to the broker's remote servers
|
||||
/// </summary>
|
||||
public override void Connect()
|
||||
{
|
||||
base.Connect();
|
||||
|
||||
AccountBaseCurrency = GetAccountBaseCurrency();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the websockets connection
|
||||
/// </summary>
|
||||
@@ -433,6 +451,31 @@ namespace QuantConnect.Brokerages.GDAX
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the account base currency
|
||||
/// </summary>
|
||||
private string GetAccountBaseCurrency()
|
||||
{
|
||||
var req = new RestRequest("/accounts", Method.GET);
|
||||
GetAuthenticationToken(req);
|
||||
var response = ExecuteRestRequest(req, GdaxEndpointType.Private);
|
||||
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception($"GDAXBrokerage.GetAccountBaseCurrency(): request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
|
||||
}
|
||||
|
||||
foreach (var item in JsonConvert.DeserializeObject<Messages.Account[]>(response.Content))
|
||||
{
|
||||
if (FiatCurrencies.Contains(item.Currency))
|
||||
{
|
||||
return item.Currency;
|
||||
}
|
||||
}
|
||||
|
||||
return Currencies.USD;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
|
||||
@@ -40,7 +40,6 @@ using QuantConnect.Orders.TimeInForces;
|
||||
using QuantConnect.Securities.Option;
|
||||
using Bar = QuantConnect.Data.Market.Bar;
|
||||
using HistoryRequest = QuantConnect.Data.HistoryRequest;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
|
||||
namespace QuantConnect.Brokerages.InteractiveBrokers
|
||||
{
|
||||
@@ -131,6 +130,8 @@ namespace QuantConnect.Brokerages.InteractiveBrokers
|
||||
|
||||
private readonly bool _enableDelayedStreamingData = Config.GetBool("ib-enable-delayed-streaming-data");
|
||||
|
||||
private volatile bool _isDisposeCalled;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if we're currently connected to the broker
|
||||
/// </summary>
|
||||
@@ -905,8 +906,15 @@ namespace QuantConnect.Brokerages.InteractiveBrokers
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_isDisposeCalled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Trace("InteractiveBrokersBrokerage.Dispose(): Disposing of IB resources.");
|
||||
|
||||
_isDisposeCalled = true;
|
||||
|
||||
if (_client != null)
|
||||
{
|
||||
Disconnect();
|
||||
@@ -1317,6 +1325,8 @@ namespace QuantConnect.Brokerages.InteractiveBrokers
|
||||
/// </summary>
|
||||
private void HandleUpdateAccountValue(object sender, IB.UpdateAccountValueEventArgs e)
|
||||
{
|
||||
//Log.Trace($"HandleUpdateAccountValue(): Key:{e.Key} Value:{e.Value} Currency:{e.Currency} AccountName:{e.AccountName}");
|
||||
|
||||
try
|
||||
{
|
||||
_accountData.AccountProperties[e.Currency + ":" + e.Key] = e.Value;
|
||||
@@ -1329,6 +1339,12 @@ namespace QuantConnect.Brokerages.InteractiveBrokers
|
||||
|
||||
OnAccountChanged(new AccountEvent(e.Currency, cashBalance));
|
||||
}
|
||||
|
||||
// IB does not explicitly return the account base currency, but we can find out using exchange rates returned
|
||||
if (e.Key == AccountValueKeys.ExchangeRate && e.Currency != "BASE" && e.Value.ToDecimal() == 1)
|
||||
{
|
||||
AccountBaseCurrency = e.Currency;
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
@@ -3023,7 +3039,7 @@ namespace QuantConnect.Brokerages.InteractiveBrokers
|
||||
var result = _ibAutomater.GetLastStartResult();
|
||||
CheckIbAutomaterError(result, false);
|
||||
|
||||
if (!result.HasError)
|
||||
if (!result.HasError && !_isDisposeCalled)
|
||||
{
|
||||
// IBGateway was closed by the v978+ automatic logoff or it was closed manually (less likely)
|
||||
Log.Trace("InteractiveBrokersBrokerage.OnIbAutomaterExited(): IBGateway close detected, restarting IBAutomater and reconnecting...");
|
||||
@@ -3062,8 +3078,7 @@ namespace QuantConnect.Brokerages.InteractiveBrokers
|
||||
private static class AccountValueKeys
|
||||
{
|
||||
public const string CashBalance = "CashBalance";
|
||||
// public const string AccruedCash = "AccruedCash";
|
||||
// public const string NetLiquidationByCurrency = "NetLiquidationByCurrency";
|
||||
public const string ExchangeRate = "ExchangeRate";
|
||||
}
|
||||
|
||||
// these are fatal errors from IB
|
||||
|
||||
@@ -20,7 +20,6 @@ using System.Linq;
|
||||
using NodaTime;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Packets;
|
||||
@@ -61,11 +60,7 @@ namespace QuantConnect.Brokerages.Oanda
|
||||
if (environment != Environment.Trade && environment != Environment.Practice)
|
||||
throw new NotSupportedException("Oanda Environment not supported: " + environment);
|
||||
|
||||
// Use v20 REST API only if you have a v20 account
|
||||
// Use v1 REST API if your account id contains only digits(ie. 2534253) as it is a legacy account
|
||||
_api = IsLegacyAccount(accountId) ? (OandaRestApiBase)
|
||||
new OandaRestApiV1(_symbolMapper, orderProvider, securityProvider, aggregator, environment, accessToken, accountId, agent) :
|
||||
new OandaRestApiV20(_symbolMapper, orderProvider, securityProvider, aggregator, environment, accessToken, accountId, agent);
|
||||
_api = new OandaRestApiV20(_symbolMapper, orderProvider, securityProvider, aggregator, environment, accessToken, accountId, agent);
|
||||
|
||||
// forward events received from API
|
||||
_api.OrderStatusChanged += (sender, orderEvent) => OnOrderEvent(orderEvent);
|
||||
@@ -83,6 +78,11 @@ namespace QuantConnect.Brokerages.Oanda
|
||||
get { return _api.IsConnected; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the brokerage account's base currency
|
||||
/// </summary>
|
||||
public override string AccountBaseCurrency => _api.AccountBaseCurrency;
|
||||
|
||||
/// <summary>
|
||||
/// Connects the client to the broker's remote servers
|
||||
/// </summary>
|
||||
@@ -322,11 +322,5 @@ namespace QuantConnect.Brokerages.Oanda
|
||||
{
|
||||
return _api.DownloadQuoteBars(symbol, startTimeUtc, endTimeUtc, resolution, requestedTimeZone);
|
||||
}
|
||||
|
||||
private static bool IsLegacyAccount(string accountId)
|
||||
{
|
||||
long value;
|
||||
return long.TryParse(accountId, out value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ using QuantConnect.Orders;
|
||||
using QuantConnect.Packets;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Util;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda
|
||||
{
|
||||
@@ -38,8 +37,8 @@ namespace QuantConnect.Brokerages.Oanda
|
||||
public abstract class OandaRestApiBase : Brokerage, IDataQueueHandler
|
||||
{
|
||||
private static readonly TimeSpan SubscribeDelay = TimeSpan.FromMilliseconds(250);
|
||||
private ManualResetEvent _refreshEvent = new ManualResetEvent(false);
|
||||
private CancellationTokenSource _streamingCancellationTokenSource = new CancellationTokenSource();
|
||||
private readonly ManualResetEvent _refreshEvent = new ManualResetEvent(false);
|
||||
private readonly CancellationTokenSource _streamingCancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
private bool _isConnected;
|
||||
|
||||
@@ -67,11 +66,6 @@ namespace QuantConnect.Brokerages.Oanda
|
||||
/// </summary>
|
||||
protected IEnumerable<Symbol> SubscribedSymbols => _subscriptionManager.GetSubscribedSymbols();
|
||||
|
||||
/// <summary>
|
||||
/// A lock object used to synchronize access to subscribed symbols
|
||||
/// </summary>
|
||||
protected readonly object LockerSubscriptions = new object();
|
||||
|
||||
/// <summary>
|
||||
/// The symbol mapper
|
||||
/// </summary>
|
||||
@@ -151,7 +145,7 @@ namespace QuantConnect.Brokerages.Oanda
|
||||
Aggregator = aggregator;
|
||||
_subscriptionManager = new EventBasedDataQueueHandlerSubscriptionManager();
|
||||
_subscriptionManager.SubscribeImpl += (s, t) => Refresh();
|
||||
_subscriptionManager.UnsubscribeImpl += (s, t) => Refresh(); ;
|
||||
_subscriptionManager.UnsubscribeImpl += (s, t) => Refresh();
|
||||
|
||||
PricingConnectionHandler = new DefaultConnectionHandler { MaximumIdleTimeSpan = TimeSpan.FromSeconds(20) };
|
||||
PricingConnectionHandler.ConnectionLost += OnPricingConnectionLost;
|
||||
@@ -183,7 +177,7 @@ namespace QuantConnect.Brokerages.Oanda
|
||||
var symbolsToSubscribe = SubscribedSymbols;
|
||||
// restart streaming session
|
||||
SubscribeSymbols(symbolsToSubscribe);
|
||||
|
||||
|
||||
} while (!_streamingCancellationTokenSource.IsCancellationRequested);
|
||||
},
|
||||
TaskCreationOptions.LongRunning
|
||||
@@ -280,6 +274,8 @@ namespace QuantConnect.Brokerages.Oanda
|
||||
/// </summary>
|
||||
public override void Connect()
|
||||
{
|
||||
AccountBaseCurrency = GetAccountBaseCurrency();
|
||||
|
||||
// Register to the event session to receive events.
|
||||
StartTransactionStream();
|
||||
|
||||
@@ -302,6 +298,11 @@ namespace QuantConnect.Brokerages.Oanda
|
||||
_isConnected = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the account base currency
|
||||
/// </summary>
|
||||
public abstract string GetAccountBaseCurrency();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of available tradable instruments/products from Oanda
|
||||
/// </summary>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -82,6 +82,16 @@ namespace QuantConnect.Brokerages.Oanda
|
||||
_apiStreaming = new DefaultApi(basePathStreaming);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the account base currency
|
||||
/// </summary>
|
||||
public override string GetAccountBaseCurrency()
|
||||
{
|
||||
var response = _apiRest.GetAccount(Authorization, AccountId);
|
||||
|
||||
return response.Account.Currency;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of available tradable instruments/products from Oanda
|
||||
/// </summary>
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represents the Oanda Account.
|
||||
/// </summary>
|
||||
public class Account
|
||||
{
|
||||
public bool HasAccountId;
|
||||
private int _accountId;
|
||||
public int accountId
|
||||
{
|
||||
get { return _accountId; }
|
||||
set
|
||||
{
|
||||
_accountId = value;
|
||||
HasAccountId = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasAccountName;
|
||||
private string _accountName;
|
||||
public string accountName
|
||||
{
|
||||
get { return _accountName; }
|
||||
set
|
||||
{
|
||||
_accountName = value;
|
||||
HasAccountName = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasAccountCurrency;
|
||||
private string _accountCurrency;
|
||||
public string accountCurrency
|
||||
{
|
||||
get { return _accountCurrency; }
|
||||
set
|
||||
{
|
||||
_accountCurrency = value;
|
||||
HasAccountCurrency = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasMarginRate;
|
||||
private string _marginRate;
|
||||
public string marginRate
|
||||
{
|
||||
get { return _marginRate; }
|
||||
set
|
||||
{
|
||||
_marginRate = value;
|
||||
HasMarginRate = true;
|
||||
}
|
||||
}
|
||||
|
||||
[IsOptional]
|
||||
public bool HasBalance;
|
||||
private string _balance;
|
||||
public string balance
|
||||
{
|
||||
get { return _balance; }
|
||||
set
|
||||
{
|
||||
_balance = value;
|
||||
HasBalance = true;
|
||||
}
|
||||
}
|
||||
|
||||
[IsOptional]
|
||||
public bool HasUnrealizedPl;
|
||||
private string _unrealizedPl;
|
||||
public string unrealizedPl
|
||||
{
|
||||
get { return _unrealizedPl; }
|
||||
set
|
||||
{
|
||||
_unrealizedPl = value;
|
||||
HasUnrealizedPl = true;
|
||||
}
|
||||
}
|
||||
|
||||
[IsOptional]
|
||||
public bool HasRealizedPl;
|
||||
private string _realizedPl;
|
||||
public string realizedPl
|
||||
{
|
||||
get { return _realizedPl; }
|
||||
set
|
||||
{
|
||||
_realizedPl = value;
|
||||
HasRealizedPl = true;
|
||||
}
|
||||
}
|
||||
|
||||
[IsOptional]
|
||||
public bool HasMarginUsed;
|
||||
private string _marginUsed;
|
||||
public string marginUsed
|
||||
{
|
||||
get { return _marginUsed; }
|
||||
set
|
||||
{
|
||||
_marginUsed = value;
|
||||
HasMarginUsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
[IsOptional]
|
||||
public bool HasMarginAvail;
|
||||
private string _marginAvail;
|
||||
public string marginAvail
|
||||
{
|
||||
get { return _marginAvail; }
|
||||
set
|
||||
{
|
||||
_marginAvail = value;
|
||||
HasMarginAvail = true;
|
||||
}
|
||||
}
|
||||
|
||||
[IsOptional]
|
||||
public bool HasOpenTrades;
|
||||
private string _openTrades;
|
||||
public string openTrades
|
||||
{
|
||||
get { return _openTrades; }
|
||||
set
|
||||
{
|
||||
_openTrades = value;
|
||||
HasOpenTrades = true;
|
||||
}
|
||||
}
|
||||
|
||||
[IsOptional]
|
||||
public bool HasOpenOrders;
|
||||
private string _openOrders;
|
||||
public string openOrders
|
||||
{
|
||||
get { return _openOrders; }
|
||||
set
|
||||
{
|
||||
_openOrders = value;
|
||||
HasOpenOrders = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
public struct Candle
|
||||
{
|
||||
public string time { get; set; }
|
||||
public int volume { get; set; }
|
||||
public bool complete { get; set; }
|
||||
|
||||
// Midpoint candles
|
||||
public double openMid { get; set; }
|
||||
public double highMid { get; set; }
|
||||
public double lowMid { get; set; }
|
||||
public double closeMid { get; set; }
|
||||
|
||||
// Bid/Ask candles
|
||||
public double openBid { get; set; }
|
||||
public double highBid { get; set; }
|
||||
public double lowBid { get; set; }
|
||||
public double closeBid { get; set; }
|
||||
public double openAsk { get; set; }
|
||||
public double highAsk { get; set; }
|
||||
public double lowAsk { get; set; }
|
||||
public double closeAsk { get; set; }
|
||||
|
||||
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represents the web response when creating a new account with Oanda.
|
||||
/// </summary>
|
||||
public class AccountResponse
|
||||
{
|
||||
public string username;
|
||||
public string password;
|
||||
public int accountId;
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represents the web response when querying the list of accounts belong to one Oanda user.
|
||||
/// </summary>
|
||||
public class AccountsResponse
|
||||
{
|
||||
public List<Account> accounts;
|
||||
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
public class CandlesResponse
|
||||
{
|
||||
public string instrument;
|
||||
public string granularity;
|
||||
public List<Candle> candles;
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represent web response for the list of active/tradable instruments provided by Oanda.
|
||||
/// </summary>
|
||||
public class InstrumentsResponse
|
||||
{
|
||||
public List<Instrument> instruments;
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represents the web response of the current active orders from Oanda.
|
||||
/// </summary>
|
||||
public class OrdersResponse
|
||||
{
|
||||
public List<Order> orders;
|
||||
public string nextPage;
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represents the current active positions from Oanda.
|
||||
/// </summary>
|
||||
public class PositionsResponse
|
||||
{
|
||||
public List<Position> positions;
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represents the post order response from Oanda.
|
||||
/// </summary>
|
||||
public class PostOrderResponse : Response
|
||||
{
|
||||
public string instrument { get; set; }
|
||||
public string time { get; set; }
|
||||
public double? price { get; set; }
|
||||
|
||||
public Order orderOpened { get; set; }
|
||||
public TradeData tradeOpened { get; set; }
|
||||
public List<Transaction> tradesClosed { get; set; }
|
||||
public Transaction tradeReduced { get; set; }
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represent the web response of the current price of active instruments from Oanda.
|
||||
/// </summary>
|
||||
public class PricesResponse
|
||||
{
|
||||
public long time;
|
||||
public List<Price> prices;
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
public class RateStreamResponse : IHeartbeat
|
||||
{
|
||||
public Heartbeat heartbeat;
|
||||
public Price tick;
|
||||
public bool IsHeartbeat()
|
||||
{
|
||||
return (heartbeat != null);
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications.Requests
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
public class CandlesRequest : Request
|
||||
{
|
||||
public CandlesRequest()
|
||||
{
|
||||
}
|
||||
|
||||
public SmartProperty<string> instrument;
|
||||
|
||||
[IsOptional]
|
||||
[DefaultValue(EGranularity.S5)]
|
||||
public SmartProperty<EGranularity> granularity;
|
||||
|
||||
[IsOptional]
|
||||
[DefaultValue(500)]
|
||||
public SmartProperty<int> count;
|
||||
|
||||
[IsOptional]
|
||||
public SmartProperty<string> start;
|
||||
|
||||
[IsOptional]
|
||||
public SmartProperty<string> end;
|
||||
|
||||
[IsOptional]
|
||||
[DefaultValue(ECandleFormat.bidask)]
|
||||
public SmartProperty<ECandleFormat> candleFormat;
|
||||
|
||||
[IsOptional]
|
||||
//[DefaultValue(true)]
|
||||
public SmartProperty<bool> includeFirst;
|
||||
|
||||
[IsOptional]
|
||||
public SmartProperty<string> dailyAlignment;
|
||||
|
||||
[IsOptional]
|
||||
public SmartProperty<string> weeklyAlignment;
|
||||
|
||||
public override string EndPoint
|
||||
{
|
||||
get { return "candles"; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public enum ECandleFormat
|
||||
{
|
||||
bidask,
|
||||
midpoint
|
||||
}
|
||||
|
||||
public enum EGranularity
|
||||
{
|
||||
S5,
|
||||
S10,
|
||||
S15,
|
||||
S30,
|
||||
M1,
|
||||
M2,
|
||||
M3,
|
||||
M5,
|
||||
M10,
|
||||
M15,
|
||||
M30,
|
||||
H1,
|
||||
H2,
|
||||
H3,
|
||||
H4,
|
||||
H6,
|
||||
H8,
|
||||
H12,
|
||||
D,
|
||||
W,
|
||||
M
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications.Requests
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
public interface ISmartProperty
|
||||
{
|
||||
bool HasValue { get; set; }
|
||||
void SetValue(object obj);
|
||||
}
|
||||
|
||||
// Functionally very similar to System.Nullable, could possibly just replace this
|
||||
public struct SmartProperty<T> : ISmartProperty
|
||||
{
|
||||
private T _value;
|
||||
public bool HasValue { get; set; }
|
||||
|
||||
public T Value
|
||||
{
|
||||
get { return _value; }
|
||||
set
|
||||
{
|
||||
_value = value;
|
||||
HasValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator SmartProperty<T>(T value)
|
||||
{
|
||||
return new SmartProperty<T>() { Value = value };
|
||||
}
|
||||
|
||||
public static implicit operator T(SmartProperty<T> value)
|
||||
{
|
||||
return value._value;
|
||||
}
|
||||
|
||||
public void SetValue(object obj)
|
||||
{
|
||||
SetValue((T)obj);
|
||||
}
|
||||
public void SetValue(T value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// This is ugly, but c'est la vie for now
|
||||
if (_value is bool)
|
||||
{ // bool values need to be lower case to be parsed correctly
|
||||
return _value.ToString().ToLowerInvariant();
|
||||
}
|
||||
return _value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class Request
|
||||
{
|
||||
public abstract string EndPoint { get; }
|
||||
|
||||
public string GetRequestString()
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
result.Append(EndPoint);
|
||||
bool firstJoin = true;
|
||||
foreach (var declaredField in this.GetType().GetTypeInfo().DeclaredFields)
|
||||
{
|
||||
var prop = declaredField.GetValue(this);
|
||||
var smartProp = prop as ISmartProperty;
|
||||
if (smartProp != null && smartProp.HasValue)
|
||||
{
|
||||
if (firstJoin)
|
||||
{
|
||||
result.Append("?");
|
||||
firstJoin = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Append("&");
|
||||
}
|
||||
|
||||
result.Append(declaredField.Name + "=" + prop);
|
||||
}
|
||||
}
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using QuantConnect.Brokerages.Oanda.RestV1.Framework;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represents the Restful web response from Oanda.
|
||||
/// </summary>
|
||||
public class Response
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
// use reflection to display all the properties that have non default values
|
||||
StringBuilder result = new StringBuilder();
|
||||
var props = this.GetType().GetTypeInfo().DeclaredProperties;
|
||||
result.AppendLine("{");
|
||||
foreach (var prop in props)
|
||||
{
|
||||
if (prop.Name != "Content" && prop.Name != "Subtitle" && prop.Name != "Title" && prop.Name != "UniqueId")
|
||||
{
|
||||
object value = prop.GetValue(this);
|
||||
bool valueIsNull = value == null;
|
||||
object defaultValue = Common.GetDefault(prop.PropertyType);
|
||||
bool defaultValueIsNull = defaultValue == null;
|
||||
if ((valueIsNull != defaultValueIsNull) // one is null when the other isn't
|
||||
|| (!valueIsNull && (value.ToString() != defaultValue.ToString()))) // both aren't null, so compare as strings
|
||||
{
|
||||
result.AppendLine(prop.Name + " : " + prop.GetValue(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
result.AppendLine("}");
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represent the Active Trades Web Response.
|
||||
/// </summary>
|
||||
public class TradesResponse
|
||||
{
|
||||
public List<TradeData> trades;
|
||||
public string nextPage;
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represents a single event when subscribed to the streaming events.
|
||||
/// </summary>
|
||||
public class Event : IHeartbeat
|
||||
{
|
||||
public Heartbeat heartbeat { get; set; }
|
||||
public Transaction transaction { get; set; }
|
||||
public bool IsHeartbeat()
|
||||
{
|
||||
return (heartbeat != null);
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represent a Heartbeat for an <see cref="Event"/> class.
|
||||
/// </summary>
|
||||
public class Heartbeat
|
||||
{
|
||||
public string time { get; set; }
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represents the interface for the HeartBeat and RateStreamResponse class.
|
||||
/// </summary>
|
||||
public interface IHeartbeat
|
||||
{
|
||||
bool IsHeartbeat();
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represents whether a property is optional.
|
||||
/// </summary>
|
||||
public class IsOptionalAttribute : Attribute
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return "Is Optional";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents maximum value of a property.
|
||||
/// </summary>
|
||||
public class MaxValueAttribute : Attribute
|
||||
{
|
||||
public object Value { get; set; }
|
||||
public MaxValueAttribute(int i)
|
||||
{
|
||||
Value = i;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents minimum value of a property.
|
||||
/// </summary>
|
||||
public class MinValueAttribute : Attribute
|
||||
{
|
||||
public object Value { get; set; }
|
||||
public MinValueAttribute(int i)
|
||||
{
|
||||
Value = i;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a financial instrument / product provided by Oanda.
|
||||
/// </summary>
|
||||
public class Instrument
|
||||
{
|
||||
public bool HasInstrument;
|
||||
private string _instrument;
|
||||
public string instrument
|
||||
{
|
||||
get { return _instrument; }
|
||||
set
|
||||
{
|
||||
_instrument = value;
|
||||
HasInstrument = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasdisplayName;
|
||||
private string _displayName;
|
||||
public string displayName
|
||||
{
|
||||
get { return _displayName; }
|
||||
set
|
||||
{
|
||||
_displayName = value;
|
||||
HasdisplayName = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Haspip;
|
||||
private string _pip;
|
||||
public string pip
|
||||
{
|
||||
get { return _pip; }
|
||||
set
|
||||
{
|
||||
_pip = value;
|
||||
Haspip = true;
|
||||
}
|
||||
}
|
||||
|
||||
[IsOptional]
|
||||
public bool HaspipLocation;
|
||||
private int _pipLocation;
|
||||
public int pipLocation
|
||||
{
|
||||
get { return _pipLocation; }
|
||||
set
|
||||
{
|
||||
_pipLocation = value;
|
||||
HaspipLocation = true;
|
||||
}
|
||||
}
|
||||
|
||||
[IsOptional]
|
||||
public bool HasextraPrecision;
|
||||
private int _extraPrecision;
|
||||
public int extraPrecision
|
||||
{
|
||||
get { return _extraPrecision; }
|
||||
set
|
||||
{
|
||||
_extraPrecision = value;
|
||||
HasextraPrecision = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasmaxTradeUnits;
|
||||
private int _maxTradeUnits;
|
||||
public int maxTradeUnits
|
||||
{
|
||||
get { return _maxTradeUnits; }
|
||||
set
|
||||
{
|
||||
_maxTradeUnits = value;
|
||||
HasmaxTradeUnits = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represents an order on Oanda.
|
||||
/// </summary>
|
||||
public class Order : Response
|
||||
{
|
||||
public long id { get; set; }
|
||||
public string instrument { get; set; }
|
||||
public int units { get; set; }
|
||||
public string side { get; set; }
|
||||
public string type { get; set; }
|
||||
public string time { get; set; }
|
||||
public double price { get; set; }
|
||||
public double takeProfit { get; set; }
|
||||
public double stopLoss { get; set; }
|
||||
public string expiry { get; set; }
|
||||
public double upperBound { get; set; }
|
||||
public double lowerBound { get; set; }
|
||||
public int trailingStop { get; set; }
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represent a Position in Oanda.
|
||||
/// </summary>
|
||||
public class Position
|
||||
{
|
||||
public string side { get; set; }
|
||||
public string instrument { get; set; }
|
||||
public int units { get; set; }
|
||||
public double avgPrice { get; set; }
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represents the Price object creating Orders for each instrument.
|
||||
/// </summary>
|
||||
public class Price
|
||||
{
|
||||
public enum State
|
||||
{
|
||||
Default,
|
||||
Increasing,
|
||||
Decreasing
|
||||
};
|
||||
|
||||
public string instrument { get; set; }
|
||||
public string time;
|
||||
public double bid { get; set; }
|
||||
public double ask { get; set; }
|
||||
public string status;
|
||||
public State state = State.Default;
|
||||
|
||||
public void update( Price update )
|
||||
{
|
||||
if ( this.bid > update.bid )
|
||||
{
|
||||
state = State.Decreasing;
|
||||
}
|
||||
else if ( this.bid < update.bid )
|
||||
{
|
||||
state = State.Increasing;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.Default;
|
||||
}
|
||||
|
||||
this.bid = update.bid;
|
||||
this.ask = update.ask;
|
||||
this.time = update.time;
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2012-2013 OANDA Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
* the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using QuantConnect.Brokerages.Oanda.RestV1.DataType.Communications;
|
||||
|
||||
namespace QuantConnect.Brokerages.Oanda.RestV1.DataType
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
/// <summary>
|
||||
/// Represents a Trade Data object containing the details of a trade.
|
||||
/// </summary>
|
||||
public class TradeData : Response
|
||||
{
|
||||
public long id { get; set; }
|
||||
public int units { get; set; }
|
||||
public string side { get; set; }
|
||||
public string instrument { get; set; }
|
||||
public string time { get; set; }
|
||||
public double price { get; set; }
|
||||
public double takeProfit { get; set; }
|
||||
public double stopLoss { get; set; }
|
||||
public int trailingStop { get; set; }
|
||||
public double trailingAmount { get; set; }
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user