Compare commits

..

1 Commits
16657 ... 16640

Author SHA1 Message Date
Martin Molinero
9ec60cb994 Improve shutdown 2024-09-19 17:59:04 -03:00
32 changed files with 108 additions and 1501 deletions

View File

@@ -38,18 +38,6 @@ namespace QuantConnect.Algorithm.CSharp
AddEquity("IBM");
AddCommand<BoolCommand>();
AddCommand<VoidCommand>();
var potentialCommand = new VoidCommand
{
Target = new[] { "BAC" },
Quantity = 10,
Parameters = new() { { "tag", "Signal X" } }
};
var commandLink = Link(potentialCommand);
Notify.Email("email@address", "Trade Command Event", $"Signal X trade\nFollow link to trigger: {commandLink}");
var commandLink2 = Link(new { Symbol = "SPY", Parameters = new Dictionary<string, int>() { { "Quantity", 10 } } });
Notify.Email("email@address", "Untyped Command Event", $"Signal Y trade\nFollow link to trigger: {commandLink2}");
}
/// <summary>

View File

@@ -1,128 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Option.StrategyMatcher;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Regression algorithm exercising an equity Long Call Backspread option strategy and asserting it's being detected by Lean and works as expected
/// </summary>
public class OptionEquityCallBackspreadRegressionAlgorithm : OptionEquityBaseStrategyRegressionAlgorithm
{
/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// </summary>
/// <param name="slice">Slice object keyed by symbol containing the stock data</param>
public override void OnData(Slice slice)
{
if (!Portfolio.Invested)
{
OptionChain chain;
if (IsMarketOpen(_optionSymbol) && slice.OptionChains.TryGetValue(_optionSymbol, out chain))
{
var callContracts = chain
.Where(contract => contract.Right == OptionRight.Call);
var expiry = callContracts.Min(x => x.Expiry);
callContracts = callContracts.Where(x => x.Expiry == expiry)
.OrderBy(x => x.Strike)
.ToList();
var strike = callContracts.Select(x => x.Strike).Distinct();
if (strike.Count() < 2) return;
var lowStrikeCall = callContracts.First();
var highStrikeCall = callContracts.First(contract => contract.Strike > lowStrikeCall.Strike && contract.Expiry == expiry);
var initialMargin = Portfolio.MarginRemaining;
var optionStrategy = OptionStrategies.CallBackspread(_optionSymbol, lowStrikeCall.Strike, highStrikeCall.Strike, expiry);
Buy(optionStrategy, 5);
var freeMarginPostTrade = Portfolio.MarginRemaining;
// It is a combination of bear call spread and long call
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.BearCallSpread.Name, 5);
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.NakedCall.Name, 5);
// Should only involve the bear call spread part
var expectedMarginUsage = (highStrikeCall.Strike - lowStrikeCall.Strike) * Securities[highStrikeCall.Symbol].SymbolProperties.ContractMultiplier * 5;
if (expectedMarginUsage != Portfolio.TotalMarginUsed)
{
throw new Exception($"Unexpect margin used!:{Portfolio.TotalMarginUsed}");
}
// we payed the ask and value using the assets price
var priceLadderDifference = GetPriceSpreadDifference(lowStrikeCall.Symbol, highStrikeCall.Symbol);
if (initialMargin != (freeMarginPostTrade + expectedMarginUsage + _paidFees - priceLadderDifference))
{
throw new Exception("Unexpect margin remaining!");
}
}
}
}
/// <summary>
/// Data Points count of all timeslices of algorithm
/// </summary>
public override long DataPoints => 15023;
/// <summary>
/// Data Points count of the algorithm history
/// </summary>
public override int AlgorithmHistoryDataPoints => 0;
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public override Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Orders", "2"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0%"},
{"Drawdown", "0%"},
{"Expectancy", "0"},
{"Start Equity", "200000"},
{"End Equity", "198565.25"},
{"Net Profit", "0%"},
{"Sharpe Ratio", "0"},
{"Sortino 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", "$9.75"},
{"Estimated Strategy Capacity", "$47000.00"},
{"Lowest Capacity Asset", "GOOCV W78ZERHAOVVQ|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "11.81%"},
{"OrderListHash", "6ece6c59826ea66fa7b0a1094a0021c7"}
};
}
}

View File

@@ -1,128 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Option.StrategyMatcher;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Regression algorithm exercising an equity Long Put Backspread option strategy and asserting it's being detected by Lean and works as expected
/// </summary>
public class OptionEquityPutBackspreadRegressionAlgorithm : OptionEquityBaseStrategyRegressionAlgorithm
{
/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// </summary>
/// <param name="slice">Slice object keyed by symbol containing the stock data</param>
public override void OnData(Slice slice)
{
if (!Portfolio.Invested)
{
OptionChain chain;
if (IsMarketOpen(_optionSymbol) && slice.OptionChains.TryGetValue(_optionSymbol, out chain))
{
var putContracts = chain
.Where(contract => contract.Right == OptionRight.Put);
var expiry = putContracts.Min(x => x.Expiry);
putContracts = putContracts.Where(x => x.Expiry == expiry)
.OrderBy(x => x.Strike)
.ToList();
var strike = putContracts.Select(x => x.Strike).Distinct();
if (strike.Count() < 2) return;
var lowStrikePut = putContracts.First();
var highStrikePut = putContracts.First(contract => contract.Strike > lowStrikePut.Strike && contract.Expiry == lowStrikePut.Expiry);
var initialMargin = Portfolio.MarginRemaining;
var optionStrategy = OptionStrategies.PutBackspread(_optionSymbol, highStrikePut.Strike, lowStrikePut.Strike, expiry);
Buy(optionStrategy, 5);
var freeMarginPostTrade = Portfolio.MarginRemaining;
// It is a combination of bull put spread and long put
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.BullPutSpread.Name, 5);
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.NakedPut.Name, 5);
// Should only involve the bull put spread part
var expectedMarginUsage = (highStrikePut.Strike - lowStrikePut.Strike) * Securities[highStrikePut.Symbol].SymbolProperties.ContractMultiplier * 5;
if (expectedMarginUsage != Portfolio.TotalMarginUsed)
{
throw new Exception("Unexpect margin used!");
}
// we payed the ask and value using the assets price
var priceLadderDifference = GetPriceSpreadDifference(lowStrikePut.Symbol, highStrikePut.Symbol);
if (initialMargin != (freeMarginPostTrade + expectedMarginUsage + _paidFees - priceLadderDifference))
{
throw new Exception("Unexpect margin remaining!");
}
}
}
}
/// <summary>
/// Data Points count of all timeslices of algorithm
/// </summary>
public override long DataPoints => 15023;
/// <summary>
/// Data Points count of the algorithm history
/// </summary>
public override int AlgorithmHistoryDataPoints => 0;
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public override Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Orders", "2"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0%"},
{"Drawdown", "0%"},
{"Expectancy", "0"},
{"Start Equity", "200000"},
{"End Equity", "199015.25"},
{"Net Profit", "0%"},
{"Sharpe Ratio", "0"},
{"Sortino 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", "$9.75"},
{"Estimated Strategy Capacity", "$1100000.00"},
{"Lowest Capacity Asset", "GOOCV 306CZL2DIL4G6|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "9.15%"},
{"OrderListHash", "1a51f04db9201f960dc04668b7f5d41d"}
};
}
}

View File

@@ -1,130 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Securities;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Option.StrategyMatcher;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Regression algorithm exercising an equity Short Call Backspread option strategy and asserting it's being detected by Lean and works as expected
/// </summary>
public class OptionEquityShortCallBackspreadRegressionAlgorithm : OptionEquityBaseStrategyRegressionAlgorithm
{
/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// </summary>
/// <param name="slice">Slice object keyed by symbol containing the stock data</param>
public override void OnData(Slice slice)
{
if (!Portfolio.Invested)
{
OptionChain chain;
if (IsMarketOpen(_optionSymbol) && slice.OptionChains.TryGetValue(_optionSymbol, out chain))
{
var callContracts = chain
.Where(contract => contract.Right == OptionRight.Call);
var expiry = callContracts.Min(x => x.Expiry);
callContracts = callContracts.Where(x => x.Expiry == expiry)
.OrderBy(x => x.Strike)
.ToList();
var strike = callContracts.Select(x => x.Strike).Distinct();
if (strike.Count() < 2) return;
var lowStrikeCall = callContracts.First();
var highStrikeCall = callContracts.First(contract => contract.Strike > lowStrikeCall.Strike && contract.Expiry == lowStrikeCall.Expiry);
var initialMargin = Portfolio.MarginRemaining;
var optionStrategy = OptionStrategies.ShortCallBackspread(_optionSymbol, lowStrikeCall.Strike, highStrikeCall.Strike, expiry);
Buy(optionStrategy, 5);
var freeMarginPostTrade = Portfolio.MarginRemaining;
// It is a combination of bull call spread and naked call
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.BullCallSpread.Name, 5);
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.NakedCall.Name, 5);
// Should only involve the naked call part
var security = Securities[highStrikeCall.Symbol];
var expectedMarginUsage = security.BuyingPowerModel.GetMaintenanceMargin(MaintenanceMarginParameters.ForQuantityAtCurrentPrice(security, -5)).Value;
if (expectedMarginUsage != Portfolio.TotalMarginUsed)
{
throw new Exception("Unexpect margin used!");
}
// we payed the ask and value using the assets price
var priceLadderDifference = GetPriceSpreadDifference(lowStrikeCall.Symbol, highStrikeCall.Symbol);
if (initialMargin != (freeMarginPostTrade + expectedMarginUsage + _paidFees - priceLadderDifference))
{
throw new Exception("Unexpect margin remaining!");
}
}
}
}
/// <summary>
/// Data Points count of all timeslices of algorithm
/// </summary>
public override long DataPoints => 15023;
/// <summary>
/// Data Points count of the algorithm history
/// </summary>
public override int AlgorithmHistoryDataPoints => 0;
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public override Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Orders", "2"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0%"},
{"Drawdown", "0%"},
{"Expectancy", "0"},
{"Start Equity", "200000"},
{"End Equity", "199915.25"},
{"Net Profit", "0%"},
{"Sharpe Ratio", "0"},
{"Sortino 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", "$9.75"},
{"Estimated Strategy Capacity", "$53000.00"},
{"Lowest Capacity Asset", "GOOCV W78ZERHAOVVQ|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "11.48%"},
{"OrderListHash", "357f13ed9e71c4dd8bb8e51e339ba7c5"}
};
}
}

View File

@@ -1,130 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Securities;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Option.StrategyMatcher;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Regression algorithm exercising an equity Short Put Backspread option strategy and asserting it's being detected by Lean and works as expected
/// </summary>
public class OptionEquityShortPutBackspreadRegressionAlgorithm : OptionEquityBaseStrategyRegressionAlgorithm
{
/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// </summary>
/// <param name="slice">Slice object keyed by symbol containing the stock data</param>
public override void OnData(Slice slice)
{
if (!Portfolio.Invested)
{
OptionChain chain;
if (IsMarketOpen(_optionSymbol) && slice.OptionChains.TryGetValue(_optionSymbol, out chain))
{
var putContracts = chain
.Where(contract => contract.Right == OptionRight.Put);
var expiry = putContracts.Min(x => x.Expiry);
putContracts = putContracts.Where(x => x.Expiry == expiry)
.OrderBy(x => x.Strike)
.ToList();
var strike = putContracts.Select(x => x.Strike).Distinct();
if (strike.Count() < 2) return;
var lowStrikePut = putContracts.First();
var highStrikePut = putContracts.First(contract => contract.Strike > lowStrikePut.Strike && contract.Expiry == lowStrikePut.Expiry);
var initialMargin = Portfolio.MarginRemaining;
var optionStrategy = OptionStrategies.ShortPutBackspread(_optionSymbol, highStrikePut.Strike, lowStrikePut.Strike, expiry);
Buy(optionStrategy, 5);
var freeMarginPostTrade = Portfolio.MarginRemaining;
// It is a combination of bear put spread and naked put
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.BearPutSpread.Name, 5);
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.NakedPut.Name, 5);
// Should only involve the naked put part
var security = Securities[lowStrikePut.Symbol];
var expectedMarginUsage = security.BuyingPowerModel.GetMaintenanceMargin(MaintenanceMarginParameters.ForQuantityAtCurrentPrice(security, -5)).Value;
if (expectedMarginUsage != Portfolio.TotalMarginUsed)
{
throw new Exception("Unexpect margin used!");
}
// we payed the ask and value using the assets price
var priceLadderDifference = GetPriceSpreadDifference(lowStrikePut.Symbol, highStrikePut.Symbol);
if (initialMargin != (freeMarginPostTrade + expectedMarginUsage + _paidFees - priceLadderDifference))
{
throw new Exception("Unexpect margin remaining!");
}
}
}
}
/// <summary>
/// Data Points count of all timeslices of algorithm
/// </summary>
public override long DataPoints => 15023;
/// <summary>
/// Data Points count of the algorithm history
/// </summary>
public override int AlgorithmHistoryDataPoints => 0;
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public override Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Orders", "2"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0%"},
{"Drawdown", "0%"},
{"Expectancy", "0"},
{"Start Equity", "200000"},
{"End Equity", "199165.25"},
{"Net Profit", "0%"},
{"Sharpe Ratio", "0"},
{"Sortino 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", "$9.75"},
{"Estimated Strategy Capacity", "$1200000.00"},
{"Lowest Capacity Asset", "GOOCV 306CZL2DIL4G6|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "8.84%"},
{"OrderListHash", "7294da06231632975e97c57721d26442"}
};
}
}

View File

@@ -37,7 +37,6 @@ class BoolCommand(Command):
def run(self, algo: QCAlgorithm) -> bool | None:
trade_ibm = self.my_custom_method()
if trade_ibm:
algo.debug(f"BoolCommand.run: {str(self)}")
algo.buy("IBM", 1)
return trade_ibm
@@ -69,18 +68,6 @@ class CallbackCommandRegressionAlgorithm(QCAlgorithm):
if not threw_exception:
raise ValueError('InvalidCommand did not throw!')
potential_command = VoidCommand()
potential_command.target = [ "BAC" ]
potential_command.quantity = 10
potential_command.parameters = { "tag": "Signal X" }
command_link = self.link(potential_command)
self.notify.email("email@address", "Trade Command Event", f"Signal X trade\nFollow link to trigger: {command_link}")
untyped_command_link = self.link({ "symbol": "SPY", "parameters": { "quantity": 10 } })
self.notify.email("email@address", "Untyped Command Event", f"Signal Y trade\nFollow link to trigger: {untyped_command_link}")
def on_command(self, data):
self.debug(f"on_command: {str(data)}")
self.buy(data.symbol, data.parameters["quantity"])
return True # False, None

View File

@@ -177,7 +177,7 @@ namespace QuantConnect.Algorithm
var startTimeUtc = CreateBarCountHistoryRequests(symbols, _warmupBarCount.Value, Settings.WarmupResolution)
.DefaultIfEmpty()
.Min(request => request == null ? default : request.StartTimeUtc);
if (startTimeUtc != default)
if(startTimeUtc != default)
{
result = startTimeUtc.ConvertFromUtc(TimeZone);
return true;
@@ -353,7 +353,7 @@ namespace QuantConnect.Algorithm
/// <returns>An enumerable of slice containing the requested historical data</returns>
[DocumentationAttribute(HistoricalData)]
public IEnumerable<DataDictionary<T>> History<T>(TimeSpan span, Resolution? resolution = null, bool? fillForward = null,
bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null,
bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode ? dataNormalizationMode = null,
int? contractDepthOffset = null)
where T : IBaseData
{
@@ -518,7 +518,7 @@ namespace QuantConnect.Algorithm
{
resolution = GetResolution(symbol, resolution, typeof(T));
CheckPeriodBasedHistoryRequestResolution(new[] { symbol }, resolution, typeof(T));
var requests = CreateBarCountHistoryRequests(new[] { symbol }, typeof(T), periods, resolution, fillForward, extendedMarketHours,
var requests = CreateBarCountHistoryRequests(new [] { symbol }, typeof(T), periods, resolution, fillForward, extendedMarketHours,
dataMappingMode, dataNormalizationMode, contractDepthOffset);
return GetDataTypedHistory<T>(requests, symbol);
}
@@ -948,19 +948,9 @@ namespace QuantConnect.Algorithm
Resolution? resolution = null, bool? fillForward = null, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null,
DataNormalizationMode? dataNormalizationMode = null, int? contractDepthOffset = null)
{
// Materialize the symbols to avoid multiple enumeration
var symbolsArray = symbols.ToArray();
return CreateDateRangeHistoryRequests(
symbolsArray,
Extensions.GetCustomDataTypeFromSymbols(symbolsArray),
startAlgoTz,
endAlgoTz,
resolution,
fillForward,
extendedMarketHours,
dataMappingMode,
dataNormalizationMode,
contractDepthOffset);
var arrayOfSymbols = symbols.ToArray();
return CreateDateRangeHistoryRequests(symbols, Extensions.GetCustomDataTypeFromSymbols(arrayOfSymbols) ?? typeof(BaseData), startAlgoTz, endAlgoTz, resolution, fillForward, extendedMarketHours,
dataMappingMode, dataNormalizationMode, contractDepthOffset);
}
/// <summary>
@@ -992,18 +982,8 @@ namespace QuantConnect.Algorithm
bool? fillForward = null, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null,
DataNormalizationMode? dataNormalizationMode = null, int? contractDepthOffset = null)
{
// Materialize the symbols to avoid multiple enumeration
var symbolsArray = symbols.ToArray();
return CreateBarCountHistoryRequests(
symbolsArray,
Extensions.GetCustomDataTypeFromSymbols(symbolsArray),
periods,
resolution,
fillForward,
extendedMarketHours,
dataMappingMode,
dataNormalizationMode,
contractDepthOffset);
return CreateBarCountHistoryRequests(symbols, typeof(BaseData), periods, resolution, fillForward, extendedMarketHours, dataMappingMode,
dataNormalizationMode, contractDepthOffset);
}
/// <summary>
@@ -1015,26 +995,20 @@ namespace QuantConnect.Algorithm
{
return symbols.Where(HistoryRequestValid).SelectMany(symbol =>
{
// Match or create configs for the symbol
var res = GetResolution(symbol, resolution, requestedType);
var exchange = GetExchangeHours(symbol, requestedType);
var configs = GetMatchingSubscriptions(symbol, requestedType, resolution).ToList();
if (configs.Count == 0)
{
return Enumerable.Empty<HistoryRequest>();
}
return configs.Select(config =>
{
// If no requested type was passed, use the config type to get the resolution (if not provided) and the exchange hours
var type = requestedType ?? config.Type;
var res = GetResolution(symbol, resolution, type);
var exchange = GetExchangeHours(symbol, type);
var start = _historyRequestFactory.GetStartTimeAlgoTz(symbol, periods, res, exchange, config.DataTimeZone,
config.Type, extendedMarketHours);
var end = Time;
var config = configs.First();
var start = _historyRequestFactory.GetStartTimeAlgoTz(symbol, periods, res, exchange, config.DataTimeZone, config.Type, extendedMarketHours);
var end = Time;
return _historyRequestFactory.CreateHistoryRequest(config, start, end, exchange, res, fillForward,
extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset);
});
return configs.Select(config => _historyRequestFactory.CreateHistoryRequest(config, start, end, exchange, res, fillForward,
extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset));
});
}
@@ -1071,7 +1045,7 @@ namespace QuantConnect.Algorithm
// if we have any user defined subscription configuration we use it, else we use internal ones if any
List<SubscriptionDataConfig> configs = null;
if (userConfig.Count != 0)
if(userConfig.Count != 0)
{
configs = userConfig;
}
@@ -1104,14 +1078,13 @@ namespace QuantConnect.Algorithm
}
else
{
var entry = MarketHoursDatabase.GetEntry(symbol, new []{ type });
resolution = GetResolution(symbol, resolution, type);
// If type was specified and not a lean data type and also not abstract, we create a new subscription
if (type != null && !LeanData.IsCommonLeanDataType(type) && !type.IsAbstract)
if (!LeanData.IsCommonLeanDataType(type) && !type.IsAbstract)
{
// we already know it's not a common lean data type
var isCustom = Extensions.IsCustomDataType(symbol, type);
var entry = MarketHoursDatabase.GetEntry(symbol, new[] { type });
// we were giving a specific type let's fetch it
return new[] { new SubscriptionDataConfig(
@@ -1132,26 +1105,19 @@ namespace QuantConnect.Algorithm
return SubscriptionManager
.LookupSubscriptionConfigDataTypes(symbol.SecurityType, resolution.Value, symbol.IsCanonical())
.Where(tuple => SubscriptionDataConfigTypeFilter(type, tuple.Item1))
.Select(x =>
{
var configType = x.Item1;
// Use the config type to get an accurate mhdb entry
var entry = MarketHoursDatabase.GetEntry(symbol, new[] { configType });
return new SubscriptionDataConfig(
configType,
symbol,
resolution.Value,
entry.DataTimeZone,
entry.ExchangeHours.TimeZone,
UniverseSettings.FillForward,
UniverseSettings.ExtendedMarketHours,
true,
false,
x.Item2,
true,
UniverseSettings.GetUniverseNormalizationModeOrDefault(symbol.SecurityType));
})
.Select(x => new SubscriptionDataConfig(
x.Item1,
symbol,
resolution.Value,
entry.DataTimeZone,
entry.ExchangeHours.TimeZone,
UniverseSettings.FillForward,
UniverseSettings.ExtendedMarketHours,
true,
false,
x.Item2,
true,
UniverseSettings.GetUniverseNormalizationModeOrDefault(symbol.SecurityType)))
// lets make sure to respect the order of the data types, if used on a history request will affect outcome when using pushthrough for example
.OrderByDescending(config => GetTickTypeOrder(config.SecurityType, config.TickType));
}
@@ -1164,11 +1130,6 @@ namespace QuantConnect.Algorithm
/// This is useful to filter OpenInterest by default from history requests unless it's explicitly requested</remarks>
private bool SubscriptionDataConfigTypeFilter(Type targetType, Type configType)
{
if (targetType == null)
{
return configType != typeof(OpenInterest);
}
var targetIsGenericType = targetType == typeof(BaseData);
return targetType.IsAssignableFrom(configType) && (!targetIsGenericType || configType != typeof(OpenInterest));
@@ -1227,7 +1188,7 @@ namespace QuantConnect.Algorithm
}
else
{
if (resolution != null)
if(resolution != null)
{
return resolution.Value;
}

View File

@@ -26,7 +26,6 @@ using Python.Runtime;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Data.Fundamental;
using System.Linq;
using Newtonsoft.Json;
using QuantConnect.Brokerages;
using QuantConnect.Scheduling;
using QuantConnect.Util;
@@ -1639,23 +1638,6 @@ namespace QuantConnect.Algorithm
};
}
/// <summary>
/// Get an authenticated link to execute the given command instance
/// </summary>
/// <param name="command">The target command</param>
/// <returns>The authenticated link</returns>
public string Link(PyObject command)
{
using var _ = Py.GIL();
var strResult = CommandPythonWrapper.Serialize(command);
using var pyType = command.GetPythonType();
var wrappedType = Extensions.CreateType(pyType);
var payload = JsonConvert.DeserializeObject<Dictionary<string, object>>(strResult);
return CommandLink(wrappedType.Name, payload);
}
/// <summary>
/// Gets indicator base type
/// </summary>

View File

@@ -3390,21 +3390,6 @@ namespace QuantConnect.Algorithm
return new DataHistory<OptionUniverse>(optionChain, new Lazy<PyObject>(() => PandasConverter.GetDataFrame(optionChain)));
}
/// <summary>
/// Get an authenticated link to execute the given command instance
/// </summary>
/// <param name="command">The target command</param>
/// <returns>The authenticated link</returns>
public string Link(object command)
{
var typeName = command.GetType().Name;
if (command is Command || typeName.Contains("AnonymousType", StringComparison.InvariantCultureIgnoreCase))
{
return CommandLink(typeName, command);
}
return string.Empty;
}
/// <summary>
/// Register a command type to be used
/// </summary>
@@ -3461,16 +3446,6 @@ namespace QuantConnect.Algorithm
return true;
}
private string CommandLink(string typeName, object command)
{
var payload = new Dictionary<string, dynamic> { { "projectId", ProjectId }, { "command", command } };
if (_registeredCommands.ContainsKey(typeName))
{
payload["command[$type]"] = typeName;
}
return Api.Authentication.Link("live/commands/create", payload);
}
private static Symbol GetCanonicalOptionSymbol(Symbol symbol)
{
// We got the underlying

View File

@@ -35,7 +35,7 @@ namespace QuantConnect.Algorithm.Selection
/// <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(AdjustUniverseConfiguration(configuration), universeSettings, Time.EndOfTimeTimeSpan,
: base(configuration, universeSettings, Time.EndOfTimeTimeSpan,
// Argument isn't used since we override 'SelectSymbols'
Enumerable.Empty<Symbol>())
{
@@ -95,13 +95,5 @@ namespace QuantConnect.Algorithm.Selection
return new Symbol(sid, ticker);
}
/// <summary>
/// Make sure the configuration of the universe is what we want
/// </summary>
private static SubscriptionDataConfig AdjustUniverseConfiguration(SubscriptionDataConfig input)
{
return new SubscriptionDataConfig(input, fillForward: false);
}
}
}

View File

@@ -1,126 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Web;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace QuantConnect.Api
{
/// <summary>
/// Helper methods for api authentication and interaction
/// </summary>
public static class Authentication
{
/// <summary>
/// Generate a secure hash for the authorization headers.
/// </summary>
/// <returns>Time based hash of user token and timestamp.</returns>
public static string Hash(int timestamp)
{
return Hash(timestamp, Globals.UserToken);
}
/// <summary>
/// Generate a secure hash for the authorization headers.
/// </summary>
/// <returns>Time based hash of user token and timestamp.</returns>
public static string Hash(int timestamp, string token)
{
// Create a new hash using current UTC timestamp.
// Hash must be generated fresh each time.
var data = $"{token}:{timestamp.ToStringInvariant()}";
return data.ToSHA256();
}
/// <summary>
/// Create an authenticated link for the target endpoint using the optional given payload
/// </summary>
/// <param name="endpoint">The endpoint</param>
/// <param name="payload">The payload</param>
/// <returns>The authenticated link to trigger the request</returns>
public static string Link(string endpoint, IEnumerable<KeyValuePair<string, object>> payload = null)
{
var queryString = HttpUtility.ParseQueryString(string.Empty);
var timestamp = (int)Time.TimeStamp();
queryString.Add("authorization", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{Globals.UserId}:{Hash(timestamp)}")));
queryString.Add("timestamp", timestamp.ToStringInvariant());
PopulateQueryString(queryString, payload);
return $"{Globals.Api}{endpoint.RemoveFromStart("/").RemoveFromEnd("/")}?{queryString}";
}
/// <summary>
/// Helper method to populate a query string with the given payload
/// </summary>
/// <remarks>Useful for testing purposes</remarks>
public static void PopulateQueryString(NameValueCollection queryString, IEnumerable<KeyValuePair<string, object>> payload = null)
{
if (payload != null)
{
foreach (var kv in payload)
{
AddToQuery(queryString, kv);
}
}
}
/// <summary>
/// Will add the given key value pairs to the query encoded as xform data
/// </summary>
private static void AddToQuery(NameValueCollection queryString, KeyValuePair<string, object> keyValuePairs)
{
var objectType = keyValuePairs.Value.GetType();
if (objectType.IsValueType || objectType == typeof(string))
{
// straight
queryString.Add(keyValuePairs.Key, keyValuePairs.Value.ToString());
}
else
{
// let's take advantage of json to load the properties we should include
var serialized = JsonConvert.SerializeObject(keyValuePairs.Value);
foreach (var jObject in JObject.Parse(serialized))
{
var subKey = $"{keyValuePairs.Key}[{jObject.Key}]";
if (jObject.Value is JObject)
{
// inception
AddToQuery(queryString, new KeyValuePair<string, object>(subKey, jObject.Value.ToObject<object>()));
}
else if(jObject.Value is JArray jArray)
{
var counter = 0;
foreach (var value in jArray.ToObject<List<object>>())
{
queryString.Add($"{subKey}[{counter++}]", value.ToString());
}
}
else
{
queryString.Add(subKey, jObject.Value.ToString());
}
}
}
}
}
}

View File

@@ -30,8 +30,7 @@ namespace QuantConnect.Brokerages
/// </summary>
private readonly Dictionary<SecurityType, HashSet<OrderType>> _supportOrderTypeBySecurityType = new()
{
{ SecurityType.Equity, new HashSet<OrderType> { OrderType.Market, OrderType.Limit, OrderType.StopMarket, OrderType.StopLimit,
OrderType.TrailingStop, OrderType.MarketOnOpen, OrderType.MarketOnClose } },
{ SecurityType.Equity, new HashSet<OrderType> { OrderType.Market, OrderType.Limit, OrderType.StopMarket, OrderType.StopLimit, OrderType.TrailingStop } },
// Market and limit order types see https://docs.alpaca.markets/docs/options-trading-overview
{ SecurityType.Option, new HashSet<OrderType> { OrderType.Market, OrderType.Limit } },
{ SecurityType.Crypto, new HashSet<OrderType> { OrderType.Market, OrderType.Limit, OrderType.StopLimit }}

View File

@@ -50,8 +50,6 @@ namespace QuantConnect.Brokerages
OrderType.StopLimit,
OrderType.ComboMarket,
OrderType.ComboLimit,
OrderType.MarketOnOpen,
OrderType.MarketOnClose
});
/// <summary>

View File

@@ -29,9 +29,6 @@ namespace QuantConnect.Commands
/// </summary>
public abstract class BaseCommandHandler : ICommandHandler
{
/// <summary>
/// Command json settings
/// </summary>
protected static readonly JsonSerializerSettings Settings = new() { TypeNameHandling = TypeNameHandling.All };
/// <summary>

View File

@@ -14,15 +14,13 @@
*/
using System;
using System.Linq;
using System.Dynamic;
using Newtonsoft.Json;
using QuantConnect.Data;
using System.Reflection;
using Newtonsoft.Json.Linq;
using System.Linq.Expressions;
using QuantConnect.Interfaces;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
namespace QuantConnect.Commands
{
@@ -36,17 +34,12 @@ namespace QuantConnect.Commands
private readonly Dictionary<string, object> _storage = new(StringComparer.InvariantCultureIgnoreCase);
/// <summary>
/// Useful to string representation in python
/// </summary>
protected string PayloadData { get; set; }
/// <summary>
/// Get the metaObject required for Dynamism.
/// </summary>
public sealed override DynamicMetaObject GetMetaObject(Expression parameter)
{
return new SerializableDynamicMetaObject(parameter, this, SetPropertyMethodInfo, GetPropertyMethodInfo);
return new GetSetPropertyDynamicMetaObject(parameter, this, SetPropertyMethodInfo, GetPropertyMethodInfo);
}
/// <summary>
@@ -80,20 +73,6 @@ namespace QuantConnect.Commands
{
if (!_storage.TryGetValue(name, out var value))
{
var type = GetType();
if (type != typeof(Command))
{
var propertyInfo = type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
if (propertyInfo != null)
{
return propertyInfo.GetValue(this, null);
}
var fieldInfo = type.GetField(name, BindingFlags.Public | BindingFlags.Instance);
if (fieldInfo != null)
{
return fieldInfo.GetValue(this);
}
}
throw new KeyNotFoundException($"Property with name \'{name}\' does not exist. Properties: {string.Join(", ", _storage.Keys)}");
}
return value;
@@ -108,36 +87,5 @@ namespace QuantConnect.Commands
{
throw new NotImplementedException($"Please implement the 'def run(algorithm) -> bool | None:' method");
}
/// <summary>
/// The string representation of this command
/// </summary>
public override string ToString()
{
if (!string.IsNullOrEmpty(PayloadData))
{
return PayloadData;
}
return JsonConvert.SerializeObject(this);
}
/// <summary>
/// Helper class so we can serialize a command
/// </summary>
private class SerializableDynamicMetaObject : GetSetPropertyDynamicMetaObject
{
private readonly Command _object;
public SerializableDynamicMetaObject(Expression expression, object value, MethodInfo setPropertyMethodInfo, MethodInfo getPropertyMethodInfo)
: base(expression, value, setPropertyMethodInfo, getPropertyMethodInfo)
{
_object = (Command)value;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return _object._storage.Keys.Concat(_object.GetType()
.GetMembers(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.MemberType == MemberTypes.Field || x.MemberType == MemberTypes.Property).Select(x => x.Name));
}
}
}
}

View File

@@ -85,7 +85,7 @@ namespace QuantConnect.Data
var isUniverseData = path.Contains("coarse", StringComparison.OrdinalIgnoreCase) ||
path.Contains("universe", StringComparison.OrdinalIgnoreCase);
if (e.Succeeded)
if (e.Succeded)
{
WriteLineToFile(_succeededDataRequestsWriter, path, _succeededDataRequestsFileName);
Interlocked.Increment(ref _succeededDataRequestsCount);
@@ -105,7 +105,7 @@ namespace QuantConnect.Data
if (Logging.Log.DebuggingEnabled)
{
Logging.Log.Debug($"DataMonitor.OnNewDataRequest(): Data from {path} could not be fetched, error: {e.ErrorMessage}");
Logging.Log.Debug($"DataMonitor.OnNewDataRequest(): Data from {path} could not be fetched");
}
}
}

View File

@@ -30,11 +30,6 @@ namespace QuantConnect
Reset();
}
/// <summary>
/// The base api url address to use
/// </summary>
public static string Api { get; } = "https://www.quantconnect.com/api/v2/";
/// <summary>
/// The user Id
/// </summary>

View File

@@ -30,24 +30,17 @@ namespace QuantConnect.Interfaces
/// <summary>
/// Whether the data was fetched successfully
/// </summary>
public bool Succeeded { get; }
/// <summary>
/// Any error message that occurred during the fetch
/// </summary>
public string ErrorMessage { get; }
public bool Succeded { get; }
/// <summary>
/// Initializes a new instance of the <see cref="DataProviderNewDataRequestEventArgs"/> class
/// </summary>
/// <param name="path">The path to the fetched data</param>
/// <param name="succeeded">Whether the data was fetched successfully</param>
/// <param name="errorMessage">Any error message that occured during the fetch</param>
public DataProviderNewDataRequestEventArgs(string path, bool succeeded, string errorMessage)
/// <param name="succeded">Whether the data was fetched successfully</param>
public DataProviderNewDataRequestEventArgs(string path, bool succeded)
{
Path = path;
Succeeded = succeeded;
ErrorMessage = errorMessage;
Succeded = succeded;
}
}
}

View File

@@ -27,8 +27,6 @@ namespace QuantConnect.Python
/// </summary>
public class CommandPythonWrapper : BasePythonWrapper<Command>
{
private static PyObject _linkSerializationMethod;
/// <summary>
/// Constructor for initialising the <see cref="CommandPythonWrapper"/> class with wrapped <see cref="PyObject"/> object
/// </summary>
@@ -42,13 +40,8 @@ namespace QuantConnect.Python
var instance = type.Invoke();
SetPythonInstance(instance);
if (!string.IsNullOrEmpty(data))
if (data != null)
{
if (HasAttr("PayloadData"))
{
SetProperty("PayloadData", data);
}
foreach (var kvp in JsonConvert.DeserializeObject<Dictionary<string, object>>(data))
{
if (kvp.Value is JArray jArray)
@@ -77,32 +70,5 @@ namespace QuantConnect.Python
var result = InvokeMethod(nameof(Run), algorithm);
return result.GetAndDispose<bool?>();
}
/// <summary>
/// Helper method to serialize a command instance
/// </summary>
public static string Serialize(PyObject command)
{
if (command == null)
{
return string.Empty;
}
if (_linkSerializationMethod == null)
{
var module = PyModule.FromString("python_serialization", @"from json import dumps
def serialize(target):
if not hasattr(target, '__dict__'):
# for example dictionaries
return dumps(target)
return dumps(target.__dict__)
");
_linkSerializationMethod = module.GetAttr("serialize");
}
using var _ = Py.GIL();
using var strResult = _linkSerializationMethod.Invoke(command);
return strResult.As<string>();
}
}
}

View File

@@ -55,10 +55,6 @@ namespace QuantConnect.Python
{
continue;
}
else if (member.Name is "ToString")
{
continue;
}
}
missingMembers.Add(member.Name);
}

View File

@@ -1151,129 +1151,6 @@ namespace QuantConnect.Securities.Option
return InvertStrategy(BearPutLadder(canonicalOption, higherStrike, middleStrike, lowerStrike, expiration), OptionStrategyDefinitions.BullPutLadder.Name);
}
/// <summary>
/// Method creates new Long Call Backspread strategy, that consists of two calls with the same expiration but different strikes.
/// It involves selling the lower strike call, while buying twice the number of the higher strike call.
/// </summary>
/// <param name="canonicalOption">Option symbol</param>
/// <param name="lowerStrike">The strike price of the short call</param>
/// <param name="higherStrike">The strike price of the long call</param>
/// <param name="expiration">Option expiration date</param>
/// <returns>Option strategy specification</returns>
public static OptionStrategy CallBackspread(
Symbol canonicalOption,
decimal lowerStrike,
decimal higherStrike,
DateTime expiration
)
{
CheckCanonicalOptionSymbol(canonicalOption, "CallBackspread");
CheckExpirationDate(expiration, "CallBackspread", nameof(expiration));
if (lowerStrike >= higherStrike)
{
throw new ArgumentException($"CallBackspread: strike prices must be in ascending order, {nameof(lowerStrike)}, {nameof(higherStrike)}");
}
return new OptionStrategy
{
Name = "Call Backspread",
Underlying = canonicalOption.Underlying,
CanonicalOption = canonicalOption,
OptionLegs = new List<OptionStrategy.OptionLegData>
{
new OptionStrategy.OptionLegData
{
Right = OptionRight.Call, Strike = lowerStrike, Quantity = -1, Expiration = expiration
},
new OptionStrategy.OptionLegData
{
Right = OptionRight.Call, Strike = higherStrike, Quantity = 2, Expiration = expiration
}
}
};
}
/// <summary>
/// Method creates new Long Put Backspread strategy, that consists of two puts with the same expiration but different strikes.
/// It involves selling the higher strike put, while buying twice the number of the lower strike put.
/// </summary>
/// <param name="canonicalOption">Option symbol</param>
/// <param name="higherStrike">The strike price of the short put</param>
/// <param name="lowerStrike">The strike price of the long put</param>
/// <param name="expiration">Option expiration date</param>
/// <returns>Option strategy specification</returns>
public static OptionStrategy PutBackspread(
Symbol canonicalOption,
decimal higherStrike,
decimal lowerStrike,
DateTime expiration
)
{
CheckCanonicalOptionSymbol(canonicalOption, "PutBackspread");
CheckExpirationDate(expiration, "PutBackspread", nameof(expiration));
if (higherStrike <= lowerStrike)
{
throw new ArgumentException($"PutBackspread: strike prices must be in descending order, {nameof(higherStrike)}, {nameof(lowerStrike)}");
}
return new OptionStrategy
{
Name = "Put Backspread",
Underlying = canonicalOption.Underlying,
CanonicalOption = canonicalOption,
OptionLegs = new List<OptionStrategy.OptionLegData>
{
new OptionStrategy.OptionLegData
{
Right = OptionRight.Put, Strike = higherStrike, Quantity = -1, Expiration = expiration
},
new OptionStrategy.OptionLegData
{
Right = OptionRight.Put, Strike = lowerStrike, Quantity = 2, Expiration = expiration
}
}
};
}
/// <summary>
/// Method creates new Short Call Backspread strategy, that consists of two calls with the same expiration but different strikes.
/// It involves buying the lower strike call, while shorting twice the number of the higher strike call.
/// </summary>
/// <param name="canonicalOption">Option symbol</param>
/// <param name="lowerStrike">The strike price of the long call</param>
/// <param name="higherStrike">The strike price of the short call</param>
/// <param name="expiration">Option expiration date</param>
public static OptionStrategy ShortCallBackspread(
Symbol canonicalOption,
decimal lowerStrike,
decimal higherStrike,
DateTime expiration
)
{
return InvertStrategy(CallBackspread(canonicalOption, lowerStrike, higherStrike, expiration), "Short Call Backspread");
}
/// <summary>
/// Method creates new Short Put Backspread strategy, that consists of two puts with the same expiration but different strikes.
/// It involves buying the higher strike put, while selling twice the number of the lower strike put.
/// </summary>
/// <param name="canonicalOption">Option symbol</param>
/// <param name="higherStrike">The strike price of the long put</param>
/// <param name="lowerStrike">The strike price of the short put</param>
/// <param name="expiration">Option expiration date</param>
/// <returns>Option strategy specification</returns>
public static OptionStrategy ShortPutBackspread(
Symbol canonicalOption,
decimal higherStrike,
decimal lowerStrike,
DateTime expiration
)
{
return InvertStrategy(PutBackspread(canonicalOption, higherStrike, lowerStrike, expiration), "Short Put Backspread");
}
/// <summary>
/// Checks that canonical option symbol is valid
/// </summary>

View File

@@ -73,7 +73,7 @@ namespace QuantConnect.Securities.Option
return new MaintenanceMargin(inAccountCurrency);
}
else if (_optionStrategy.Name == OptionStrategyDefinitions.CoveredCall.Name)
else if(_optionStrategy.Name == OptionStrategyDefinitions.CoveredCall.Name)
{
// MAX[In-the-money amount + Margin(long stock evaluated at min(mark price, strike(short call))), min(stock value, max(call value, long stock margin))]
var optionPosition = parameters.PositionGroup.Positions.FirstOrDefault(position => position.Symbol.SecurityType.IsOption());
@@ -208,7 +208,7 @@ namespace QuantConnect.Securities.Option
var result = GetMiddleAndLowStrikeDifference(parameters.PositionGroup, parameters.Portfolio);
return new MaintenanceMargin(result);
}
else if (_optionStrategy.Name == OptionStrategyDefinitions.IronCondor.Name || _optionStrategy.Name == OptionStrategyDefinitions.IronButterfly.Name ||
else if (_optionStrategy.Name == OptionStrategyDefinitions.IronCondor.Name || _optionStrategy.Name == OptionStrategyDefinitions.IronButterfly.Name ||
_optionStrategy.Name == OptionStrategyDefinitions.ShortIronCondor.Name || _optionStrategy.Name == OptionStrategyDefinitions.ShortIronButterfly.Name)
{
var result = GetShortPutLongPutStrikeDifferenceMargin(parameters.PositionGroup.Positions, parameters.Portfolio, parameters.PositionGroup.Quantity);
@@ -239,7 +239,7 @@ namespace QuantConnect.Securities.Option
var orderCosts = shortCallSecurity.AskPrice - longCallSecurity.BidPrice + shortPutSecurity.AskPrice - longPutSecurity.BidPrice;
var multiplier = Math.Abs(longCallPosition.Quantity) * longCallSecurity.ContractUnitOfTrade;
var closeCost = commissionFees + orderCosts * multiplier;
var strikeDifference = longCallPosition.Symbol.ID.StrikePrice - shortCallPosition.Symbol.ID.StrikePrice;
var result = Math.Max(1.02m * closeCost, strikeDifference * multiplier);
@@ -253,7 +253,7 @@ namespace QuantConnect.Securities.Option
// long calendar spread part has no margin requirement due to same strike
// only the short calendar spread's short option has margin requirement
var furtherExpiry = parameters.PositionGroup.Positions.Max(position => position.Symbol.ID.Date);
var shortCalendarSpreadShortLeg = parameters.PositionGroup.Positions.Single(position =>
var shortCalendarSpreadShortLeg = parameters.PositionGroup.Positions.Single(position =>
position.Quantity < 0 && position.Symbol.ID.Date == furtherExpiry);
var shortCalendarSpreadShortLegSecurity = (Option)parameters.Portfolio.Securities[shortCalendarSpreadShortLeg.Symbol];
var result = Math.Abs(shortCalendarSpreadShortLegSecurity.BuyingPowerModel.GetMaintenanceMargin(
@@ -302,7 +302,7 @@ namespace QuantConnect.Securities.Option
result = Math.Abs(underlyingSecurity.BuyingPowerModel.GetInitialMarginRequirement(underlyingSecurity, underlyingPosition.Quantity));
result = parameters.Portfolio.CashBook.ConvertToAccountCurrency(result, underlyingSecurity.QuoteCurrency.Symbol);
}
else if (_optionStrategy.Name == OptionStrategyDefinitions.CoveredCall.Name)
else if(_optionStrategy.Name == OptionStrategyDefinitions.CoveredCall.Name)
{
// Max(Call Value, Long Stock Initial Margin)
var optionPosition = parameters.PositionGroup.Positions.FirstOrDefault(position => position.Symbol.SecurityType.IsOption());
@@ -386,7 +386,7 @@ namespace QuantConnect.Securities.Option
{
result = GetMiddleAndLowStrikeDifference(parameters.PositionGroup, parameters.Portfolio);
}
else if (_optionStrategy.Name == OptionStrategyDefinitions.IronCondor.Name || _optionStrategy.Name == OptionStrategyDefinitions.IronButterfly.Name ||
else if (_optionStrategy.Name == OptionStrategyDefinitions.IronCondor.Name || _optionStrategy.Name == OptionStrategyDefinitions.IronButterfly.Name ||
_optionStrategy.Name == OptionStrategyDefinitions.ShortIronCondor.Name || _optionStrategy.Name == OptionStrategyDefinitions.ShortIronButterfly.Name)
{
result = GetShortPutLongPutStrikeDifferenceMargin(parameters.PositionGroup.Positions, parameters.Portfolio, parameters.PositionGroup.Quantity);
@@ -595,7 +595,7 @@ namespace QuantConnect.Securities.Option
private static decimal GetCollarConversionInitialMargin(IPositionGroup positionGroup, SecurityPortfolioManager portfolio, OptionRight optionRight)
{
// Initial Stock Margin Requirement + In the Money Call/Put Amount
var optionPosition = positionGroup.Positions.Single(position =>
var optionPosition = positionGroup.Positions.Single(position =>
position.Symbol.SecurityType.IsOption() && position.Symbol.ID.OptionRight == optionRight);
var underlyingPosition = positionGroup.Positions.Single(position => !position.Symbol.SecurityType.IsOption());
var optionSecurity = (Option)portfolio.Securities[optionPosition.Symbol];

View File

@@ -33,7 +33,7 @@ namespace QuantConnect.Securities.Option.StrategyMatcher
typeof(OptionStrategyDefinitions)
.GetProperties(BindingFlags.Public | BindingFlags.Static)
.Where(property => property.PropertyType == typeof(OptionStrategyDefinition))
.Select(property => (OptionStrategyDefinition)property.GetValue(null))
.Select(property => (OptionStrategyDefinition) property.GetValue(null))
.ToImmutableList()
);

View File

@@ -48,7 +48,6 @@ namespace QuantConnect.Lean.Engine
private IAlgorithm _algorithm;
private readonly object _lock;
private readonly bool _liveMode;
private bool _cancelRequested;
private CancellationTokenSource _cancellationTokenSource;
/// <summary>
@@ -613,20 +612,13 @@ namespace QuantConnect.Lean.Engine
_algorithm.SetStatus(state);
}
if (_cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested && !_cancelRequested)
if (state == AlgorithmStatus.Deleted)
{
if (state == AlgorithmStatus.Deleted)
if (!_cancellationTokenSource.IsCancellationRequested)
{
_cancelRequested = true;
// if the algorithm was deleted, let's give the algorithm a few seconds to shutdown and cancel it out
// if the algorithm was deleted or stopped, let's give the algorithm a few seconds to shutdown and cancel it out
_cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(5));
}
else if (state == AlgorithmStatus.Stopped)
{
_cancelRequested = true;
// if the algorithm was stopped, let's give the algorithm a few seconds to shutdown and cancel it out
_cancellationTokenSource.CancelAfter(TimeSpan.FromMinutes(1));
}
}
}
}

View File

@@ -24,8 +24,6 @@ namespace QuantConnect.Lean.Engine.DataFeeds
/// </summary>
public class DefaultDataProvider : IDataProvider, IDisposable
{
private bool _oneTimeWarningLog;
/// <summary>
/// Event raised each time data fetch is finished (successfully or not)
/// </summary>
@@ -39,7 +37,6 @@ namespace QuantConnect.Lean.Engine.DataFeeds
public virtual Stream Fetch(string key)
{
var success = true;
var errorMessage = string.Empty;
try
{
return new FileStream(FileExtension.ToNormalizedPath(key), FileMode.Open, FileAccess.Read, FileShare.Read);
@@ -47,17 +44,8 @@ namespace QuantConnect.Lean.Engine.DataFeeds
catch (Exception exception)
{
success = false;
errorMessage = exception.Message;
if (exception is DirectoryNotFoundException)
{
if (!_oneTimeWarningLog)
{
_oneTimeWarningLog = true;
Logging.Log.Debug($"DefaultDataProvider.Fetch(): DirectoryNotFoundException: please review data paths, current 'Globals.DataFolder': {Globals.DataFolder}");
}
return null;
}
else if (exception is FileNotFoundException)
if (exception is DirectoryNotFoundException
|| exception is FileNotFoundException)
{
return null;
}
@@ -66,7 +54,7 @@ namespace QuantConnect.Lean.Engine.DataFeeds
}
finally
{
OnNewDataRequest(new DataProviderNewDataRequestEventArgs(key, success, errorMessage));
OnNewDataRequest(new DataProviderNewDataRequestEventArgs(key, success));
}
}

View File

@@ -196,6 +196,11 @@ namespace QuantConnect.Lean.Engine.DataFeeds
factory = new TimeTriggeredUniverseSubscriptionEnumeratorFactory(request.Universe as ITimeTriggeredUniverse,
_marketHoursDatabase,
_timeProvider);
if (request.Universe is UserDefinedUniverse)
{
return factory.CreateEnumerator(request, _dataProvider);
}
}
else if (request.Configuration.Type == typeof(FundamentalUniverse))
{

View File

@@ -209,7 +209,7 @@ namespace QuantConnect.Lean.Engine.RealTime
/// </summary>
public override void Exit()
{
_realTimeThread.StopSafely(TimeSpan.FromMinutes(1), _cancellationTokenSource);
_realTimeThread.StopSafely(TimeSpan.FromMinutes(5), _cancellationTokenSource);
_cancellationTokenSource.DisposeSafely();
base.Exit();
}

View File

@@ -208,19 +208,18 @@
"tt-log-fix-messages": false,
// Trade Station configuration
"trade-station-client-id": "",
"trade-station-client-secret": "",
"trade-station-api-key": "",
"trade-station-api-secret": "",
"trade-station-code-from-url": "",
"trade-station-redirect-url": "http://localhost",
"trade-station-refresh-token": "",
"trade-station-api-url": "https://sim-api.tradestation.com",
"trade-station-account-type": "Cash|Margin|Futures",
"trade-station-account-id": "",
// Alpaca configuration
"alpaca-api-key": "",
"alpaca-api-secret": "",
"alpaca-access-token": "",
"alpaca-paper-trading": true,
// [Optional] Trade Station Proxy Settings
"trade-station-use-proxy": false,
"trade-station-proxy-address-port": "",
"trade-station-proxy-username": "",
"trade-station-proxy-password": "",
// Exante trading configuration
// client-id, application-id, shared-key are required to access Exante REST API
@@ -533,34 +532,6 @@
"history-provider": [ "BrokerageHistoryProvider", "SubscriptionDataReaderHistoryProvider" ]
},
"live-trade-station": {
"live-mode": true,
// real brokerage implementations require the BrokerageTransactionHandler
"live-mode-brokerage": "TradeStationBrokerage",
"data-queue-handler": [ "TradeStationBrokerage" ],
"setup-handler": "QuantConnect.Lean.Engine.Setup.BrokerageSetupHandler",
"result-handler": "QuantConnect.Lean.Engine.Results.LiveTradingResultHandler",
"data-feed-handler": "QuantConnect.Lean.Engine.DataFeeds.LiveTradingDataFeed",
"real-time-handler": "QuantConnect.Lean.Engine.RealTime.LiveTradingRealTimeHandler",
"transaction-handler": "QuantConnect.Lean.Engine.TransactionHandlers.BrokerageTransactionHandler",
"history-provider": [ "BrokerageHistoryProvider", "SubscriptionDataReaderHistoryProvider" ]
},
"live-alpaca": {
"live-mode": true,
// real brokerage implementations require the BrokerageTransactionHandler
"live-mode-brokerage": "AlpacaBrokerage",
"data-queue-handler": [ "AlpacaBrokerage" ],
"setup-handler": "QuantConnect.Lean.Engine.Setup.BrokerageSetupHandler",
"result-handler": "QuantConnect.Lean.Engine.Results.LiveTradingResultHandler",
"data-feed-handler": "QuantConnect.Lean.Engine.DataFeeds.LiveTradingDataFeed",
"real-time-handler": "QuantConnect.Lean.Engine.RealTime.LiveTradingRealTimeHandler",
"transaction-handler": "QuantConnect.Lean.Engine.TransactionHandlers.BrokerageTransactionHandler",
"history-provider": [ "BrokerageHistoryProvider", "SubscriptionDataReaderHistoryProvider" ]
},
"live-futures-bybit": {
"live-mode": true,

View File

@@ -415,135 +415,62 @@ def getTickHistory(algorithm, symbol, start, end):
Assert.AreEqual(expectedCount == 1 ? TickType.Trade : TickType.Quote, _testHistoryProvider.HistryRequests.First().TickType);
}
private static IEnumerable<TestCaseData> BarCountHistoryRequestTestCases
[TestCase(Resolution.Second, Language.CSharp, true)]
[TestCase(Resolution.Minute, Language.CSharp, true)]
[TestCase(Resolution.Hour, Language.CSharp, true)]
[TestCase(Resolution.Daily, Language.CSharp, true)]
[TestCase(Resolution.Second, Language.Python, true)]
[TestCase(Resolution.Minute, Language.Python, true)]
[TestCase(Resolution.Hour, Language.Python, true)]
[TestCase(Resolution.Daily, Language.Python, true)]
[TestCase(Resolution.Second, Language.CSharp, false)]
[TestCase(Resolution.Minute, Language.CSharp, false)]
[TestCase(Resolution.Hour, Language.CSharp, false)]
[TestCase(Resolution.Daily, Language.CSharp, false)]
[TestCase(Resolution.Second, Language.Python, false)]
[TestCase(Resolution.Minute, Language.Python, false)]
[TestCase(Resolution.Hour, Language.Python, false)]
[TestCase(Resolution.Daily, Language.Python, false)]
public void BarCountHistoryRequestIsCorrectlyBuilt(Resolution resolution, Language language, bool symbolAlreadyAdded)
{
get
{
var spyDate = new DateTime(2013, 10, 07);
yield return new TestCaseData(Resolution.Second, Language.CSharp, Symbols.SPY, true, spyDate, null, false);
yield return new TestCaseData(Resolution.Minute, Language.CSharp, Symbols.SPY, true, spyDate, null, false);
yield return new TestCaseData(Resolution.Hour, Language.CSharp, Symbols.SPY, true, spyDate, null, false);
yield return new TestCaseData(Resolution.Daily, Language.CSharp, Symbols.SPY, true, spyDate, null, false);
yield return new TestCaseData(Resolution.Second, Language.Python, Symbols.SPY, true, spyDate, null, false);
yield return new TestCaseData(Resolution.Minute, Language.Python, Symbols.SPY, true, spyDate, null, false);
yield return new TestCaseData(Resolution.Hour, Language.Python, Symbols.SPY, true, spyDate, null, false);
yield return new TestCaseData(Resolution.Daily, Language.Python, Symbols.SPY, true, spyDate, null, false);
yield return new TestCaseData(Resolution.Second, Language.CSharp, Symbols.SPY, false, spyDate, null, false);
yield return new TestCaseData(Resolution.Minute, Language.CSharp, Symbols.SPY, false, spyDate, null, false);
yield return new TestCaseData(Resolution.Hour, Language.CSharp, Symbols.SPY, false, spyDate, null, false);
yield return new TestCaseData(Resolution.Daily, Language.CSharp, Symbols.SPY, false, spyDate, null, false);
yield return new TestCaseData(Resolution.Second, Language.Python, Symbols.SPY, false, spyDate, null, false);
yield return new TestCaseData(Resolution.Minute, Language.Python, Symbols.SPY, false, spyDate, null, false);
yield return new TestCaseData(Resolution.Hour, Language.Python, Symbols.SPY, false, spyDate, null, false);
yield return new TestCaseData(Resolution.Daily, Language.Python, Symbols.SPY, false, spyDate, null, false);
yield return new TestCaseData(Resolution.Second, Language.CSharp, Symbols.SPY, true, spyDate, null, true);
yield return new TestCaseData(Resolution.Minute, Language.CSharp, Symbols.SPY, true, spyDate, null, true);
yield return new TestCaseData(Resolution.Hour, Language.CSharp, Symbols.SPY, true, spyDate, null, true);
yield return new TestCaseData(Resolution.Daily, Language.CSharp, Symbols.SPY, true, spyDate, null, true);
yield return new TestCaseData(Resolution.Second, Language.Python, Symbols.SPY, true, spyDate, null, true);
yield return new TestCaseData(Resolution.Minute, Language.Python, Symbols.SPY, true, spyDate, null, true);
yield return new TestCaseData(Resolution.Hour, Language.Python, Symbols.SPY, true, spyDate, null, true);
yield return new TestCaseData(Resolution.Daily, Language.Python, Symbols.SPY, true, spyDate, null, true);
yield return new TestCaseData(Resolution.Second, Language.CSharp, Symbols.SPY, false, spyDate, null, true);
yield return new TestCaseData(Resolution.Minute, Language.CSharp, Symbols.SPY, false, spyDate, null, true);
yield return new TestCaseData(Resolution.Hour, Language.CSharp, Symbols.SPY, false, spyDate, null, true);
yield return new TestCaseData(Resolution.Daily, Language.CSharp, Symbols.SPY, false, spyDate, null, true);
yield return new TestCaseData(Resolution.Second, Language.Python, Symbols.SPY, false, spyDate, null, true);
yield return new TestCaseData(Resolution.Minute, Language.Python, Symbols.SPY, false, spyDate, null, true);
yield return new TestCaseData(Resolution.Hour, Language.Python, Symbols.SPY, false, spyDate, null, true);
yield return new TestCaseData(Resolution.Daily, Language.Python, Symbols.SPY, false, spyDate, null, true);
var spxCanonicalOption = Symbol.CreateCanonicalOption(Symbols.SPX);
var spxDate = new DateTime(2021, 01, 12);
yield return new TestCaseData(Resolution.Daily, Language.CSharp, spxCanonicalOption, true, spxDate, null, true);
yield return new TestCaseData(Resolution.Daily, Language.Python, spxCanonicalOption, true, spxDate, null, true);
yield return new TestCaseData(null, Language.CSharp, spxCanonicalOption, true, spxDate, Resolution.Daily, true);
yield return new TestCaseData(null, Language.Python, spxCanonicalOption, true, spxDate, Resolution.Daily, true);
yield return new TestCaseData(Resolution.Daily, Language.CSharp, spxCanonicalOption, false, spxDate, null, true);
yield return new TestCaseData(Resolution.Daily, Language.Python, spxCanonicalOption, false, spxDate, null, true);
yield return new TestCaseData(null, Language.CSharp, spxCanonicalOption, false, spxDate, Resolution.Daily, true);
yield return new TestCaseData(null, Language.Python, spxCanonicalOption, false, spxDate, Resolution.Daily, true);
}
}
[TestCaseSource(nameof(BarCountHistoryRequestTestCases))]
public void BarCountHistoryRequestIsCorrectlyBuilt(Resolution? resolution, Language language, Symbol symbol,
bool symbolAlreadyAdded, DateTime dateTime, Resolution? defaultResolution, bool multiSymbol)
{
_algorithm.SetStartDate(dateTime);
_algorithm.SetStartDate(2013, 10, 07);
var symbol = Symbols.SPY;
if (symbolAlreadyAdded)
{
// it should not matter
_algorithm.AddSecurity(symbol);
_algorithm.AddEquity("SPY");
}
if (language == Language.CSharp)
{
if (multiSymbol)
{
_algorithm.History(new[] { symbol }, 10, resolution);
}
else
{
_algorithm.History(symbol, 10, resolution);
}
_algorithm.History(symbol, 10, resolution);
}
else
{
using (Py.GIL())
{
_algorithm.SetPandasConverter();
if (multiSymbol)
{
using var pySymbols = new[] { symbol }.ToPyListUnSafe();
_algorithm.History(pySymbols, 10, resolution);
pySymbols[0].Dispose();
}
else
{
using var pySymbol = symbol.ToPython();
_algorithm.History(pySymbol, 10, resolution);
}
_algorithm.History(symbol.ToPython(), 10, resolution);
}
}
Resolution? fillForwardResolution = null;
if (resolution != Resolution.Tick)
{
fillForwardResolution = resolution ?? defaultResolution;
fillForwardResolution = resolution;
}
if (symbol.SecurityType == SecurityType.Equity)
{
var expectedCount = resolution == Resolution.Hour || resolution == Resolution.Daily ? 1 : 2;
Assert.AreEqual(expectedCount, _testHistoryProvider.HistryRequests.Count);
var request = _testHistoryProvider.HistryRequests.First();
Assert.AreEqual(symbol, request.Symbol);
Assert.AreEqual(resolution, request.Resolution);
Assert.IsFalse(request.IncludeExtendedMarketHours);
Assert.IsFalse(request.IsCustomData);
Assert.AreEqual(fillForwardResolution, request.FillForwardResolution);
Assert.AreEqual(DataNormalizationMode.Adjusted, request.DataNormalizationMode);
var expectedCount = resolution == Resolution.Hour || resolution == Resolution.Daily ? 1 : 2;
Assert.AreEqual(expectedCount, _testHistoryProvider.HistryRequests.Count);
Assert.AreEqual(Symbols.SPY, _testHistoryProvider.HistryRequests.First().Symbol);
Assert.AreEqual(resolution, _testHistoryProvider.HistryRequests.First().Resolution);
Assert.IsFalse(_testHistoryProvider.HistryRequests.First().IncludeExtendedMarketHours);
Assert.IsFalse(_testHistoryProvider.HistryRequests.First().IsCustomData);
Assert.AreEqual(fillForwardResolution, _testHistoryProvider.HistryRequests.First().FillForwardResolution);
Assert.AreEqual(DataNormalizationMode.Adjusted, _testHistoryProvider.HistryRequests.First().DataNormalizationMode);
Assert.AreEqual(expectedCount == 1 ? TickType.Trade : TickType.Quote, request.TickType);
}
else if (symbol.SecurityType == SecurityType.IndexOption)
{
Assert.AreEqual(1, _testHistoryProvider.HistryRequests.Count);
var request = _testHistoryProvider.HistryRequests.Single();
Assert.AreEqual(symbol, request.Symbol);
Assert.AreEqual(resolution ?? defaultResolution, request.Resolution);
Assert.AreEqual(typeof(OptionUniverse), request.DataType);
Assert.IsFalse(request.IncludeExtendedMarketHours);
Assert.IsFalse(request.IsCustomData);
// For OptionUniverse, exchange and data time zones are set to the same value
Assert.AreEqual(request.ExchangeHours.TimeZone, request.DataTimeZone);
}
Assert.AreEqual(expectedCount == 1 ? TickType.Trade : TickType.Quote, _testHistoryProvider.HistryRequests.First().TickType);
}
[TestCase(Language.CSharp, true)]
@@ -856,15 +783,12 @@ def getOpenInterestHistory(algorithm, symbol, start, end, resolution):
{
var result = _algorithm.History(new[] { optionSymbol }, start, end, historyResolution, fillForward:false).ToList();
Assert.Multiple(() =>
{
Assert.AreEqual(53, result.Count);
Assert.IsTrue(result.Any(slice => slice.ContainsKey(optionSymbol)));
Assert.AreEqual(53, result.Count);
Assert.IsTrue(result.Any(slice => slice.ContainsKey(optionSymbol)));
var openInterests = result.Select(slice => slice.Get(typeof(OpenInterest)) as DataDictionary<OpenInterest>).Where(dataDictionary => dataDictionary.Count > 0).ToList();
var openInterests = result.Select(slice => slice.Get(typeof(OpenInterest)) as DataDictionary<OpenInterest>).Where(dataDictionary => dataDictionary.Count > 0).ToList();
Assert.AreEqual(0, openInterests.Count);
});
Assert.AreEqual(0, openInterests.Count);
}
else
{

View File

@@ -1,51 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Web;
using NUnit.Framework;
using QuantConnect.Api;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
namespace QuantConnect.Tests.API
{
[TestFixture]
public class AuthenticationTests
{
[Test, Explicit("Requires api creds")]
public void Link()
{
var link = Authentication.Link("authenticate");
var response = link.DownloadData();
Assert.IsNotNull(response);
var jobject = JObject.Parse(response);
Assert.IsTrue(jobject["success"].ToObject<bool>());
}
[Test]
public void PopulateQueryString()
{
var payload = new { SomeArray = new[] { 1, 2, 3 }, Symbol = "SPY", Parameters = new Dictionary<string, int>() { { "Quantity", 10 } } };
var queryString = HttpUtility.ParseQueryString(string.Empty);
Authentication.PopulateQueryString(queryString, new[] { new KeyValuePair<string, object>("command", payload) });
Assert.AreEqual("command[SomeArray][0]=1&command[SomeArray][1]=2&command[SomeArray][2]=3&command[Symbol]=SPY&command[Parameters][Quantity]=10", queryString.ToString());
}
}
}

View File

@@ -13,75 +13,21 @@
* limitations under the License.
*/
using System;
using System.IO;
using System.Web;
using NUnit.Framework;
using Newtonsoft.Json;
using QuantConnect.Commands;
using QuantConnect.Statistics;
using QuantConnect.Configuration;
using System.Collections.Generic;
using QuantConnect.Algorithm.CSharp;
using QuantConnect.Lean.Engine.Server;
using QuantConnect.Tests.Engine.DataFeeds;
using System;
namespace QuantConnect.Tests.Common.Commands
{
[TestFixture]
public class CallbackCommandTests
{
[Test]
public void BaseTypedLink()
{
var algorithmStub = new AlgorithmStub
{
ProjectId = 19542033
};
algorithmStub.AddCommand<MyCommand>();
var commandInstance = new MyCommand
{
Quantity = 0.1m,
Target = "BTCUSD"
};
var link = algorithmStub.Link(commandInstance);
var parse = HttpUtility.ParseQueryString(link);
Assert.IsFalse(string.IsNullOrEmpty(link));
}
[Test]
public void ComplexTypedLink()
{
var algorithmStub = new AlgorithmStub
{
ProjectId = 19542033
};
algorithmStub.AddCommand<MyCommand2>();
var commandInstance = new MyCommand2
{
Parameters = new Dictionary<string, object> { { "quantity", 0.1 } },
Target = new[] { "BTCUSD", "AAAA" }
};
var link = algorithmStub.Link(commandInstance);
var parse = HttpUtility.ParseQueryString(link);
Assert.IsFalse(string.IsNullOrEmpty(link));
}
[Test]
public void UntypedLink()
{
var algorithmStub = new AlgorithmStub
{
ProjectId = 19542033
};
var link = algorithmStub.Link(new { quantity = -0.1, target = "BTCUSD" });
var parse = HttpUtility.ParseQueryString(link);
Assert.IsFalse(string.IsNullOrEmpty(link));
}
[TestCase(Language.CSharp)]
[TestCase(Language.Python)]
public void CommanCallback(Language language)
@@ -179,17 +125,5 @@ namespace QuantConnect.Tests.Common.Commands
SetCommandHandler();
}
}
private class MyCommand2 : Command
{
public string[] Target { get; set; }
public Dictionary<string, object> Parameters;
}
private class MyCommand : Command
{
public string Target { get; set; }
public decimal Quantity;
}
}
}

View File

@@ -1412,173 +1412,5 @@ namespace QuantConnect.Tests.Common.Securities.Options
Assert.AreEqual(expiration, lowStrikeLeg.Expiration);
Assert.AreEqual(1, lowStrikeLeg.Quantity);
}
[TestCase(325, 300)]
[TestCase(300, 300)]
public void FailsBuildingCallBackspreadStrategy(decimal strike1, decimal strike2)
{
var canonicalOptionSymbol = Symbols.SPY_Option_Chain;
var underlying = Symbols.SPY;
var expiration = new DateTime(2023, 08, 18);
// Unordered and repeated strikes
Assert.Throws<ArgumentException>(
() => OptionStrategies.CallBackspread(canonicalOptionSymbol, strike1, strike2, expiration));
}
[Test]
public void BuildCallBackspreadStrategy()
{
var canonicalOptionSymbol = Symbols.SPY_Option_Chain;
var underlying = Symbols.SPY;
var strike1 = 300m;
var strike2 = 325m;
var expiration = new DateTime(2023, 08, 18);
var strategy = OptionStrategies.CallBackspread(canonicalOptionSymbol, strike1, strike2, expiration);
Assert.AreEqual("Call Backspread", strategy.Name);
Assert.AreEqual(underlying, strategy.Underlying);
Assert.AreEqual(canonicalOptionSymbol, strategy.CanonicalOption);
Assert.AreEqual(2, strategy.OptionLegs.Count);
Assert.AreEqual(0, strategy.UnderlyingLegs.Count);
var lowStrikeLeg = strategy.OptionLegs.Single(x => x.Strike == strike1);
Assert.AreEqual(OptionRight.Call, lowStrikeLeg.Right);
Assert.AreEqual(expiration, lowStrikeLeg.Expiration);
Assert.AreEqual(-1, lowStrikeLeg.Quantity);
var highStrikeLeg = strategy.OptionLegs.Single(x => x.Strike == strike2);
Assert.AreEqual(OptionRight.Call, highStrikeLeg.Right);
Assert.AreEqual(expiration, highStrikeLeg.Expiration);
Assert.AreEqual(2, highStrikeLeg.Quantity);
}
[TestCase(325, 350)]
[TestCase(300, 300)]
public void FailsBuildingPutBackspreadStrategy(decimal strike1, decimal strike2)
{
var canonicalOptionSymbol = Symbols.SPY_Option_Chain;
var underlying = Symbols.SPY;
var expiration = new DateTime(2023, 08, 18);
// Unordered and repeated strikes
Assert.Throws<ArgumentException>(
() => OptionStrategies.PutBackspread(canonicalOptionSymbol, strike1, strike2, expiration));
}
[Test]
public void BuildsPutBackspreadStrategy()
{
var canonicalOptionSymbol = Symbols.SPY_Option_Chain;
var underlying = Symbols.SPY;
var strike1 = 350m;
var strike2 = 325m;
var expiration = new DateTime(2023, 08, 18);
var strategy = OptionStrategies.PutBackspread(canonicalOptionSymbol, strike1, strike2, expiration);
Assert.AreEqual("Put Backspread", strategy.Name);
Assert.AreEqual(underlying, strategy.Underlying);
Assert.AreEqual(canonicalOptionSymbol, strategy.CanonicalOption);
Assert.AreEqual(2, strategy.OptionLegs.Count);
Assert.AreEqual(0, strategy.UnderlyingLegs.Count);
var highStrikeLeg = strategy.OptionLegs.Single(x => x.Strike == strike1);
Assert.AreEqual(OptionRight.Put, highStrikeLeg.Right);
Assert.AreEqual(expiration, highStrikeLeg.Expiration);
Assert.AreEqual(-1, highStrikeLeg.Quantity);
var lowStrikeLeg = strategy.OptionLegs.Single(x => x.Strike == strike2);
Assert.AreEqual(OptionRight.Put, lowStrikeLeg.Right);
Assert.AreEqual(expiration, lowStrikeLeg.Expiration);
Assert.AreEqual(2, lowStrikeLeg.Quantity);
}
[TestCase(325, 300)]
[TestCase(300, 300)]
public void FailsBuildingShortCallBackspreadStrategy(decimal strike1, decimal strike2)
{
var canonicalOptionSymbol = Symbols.SPY_Option_Chain;
var underlying = Symbols.SPY;
var expiration = new DateTime(2023, 08, 18);
// Unordered and repeated strikes
Assert.Throws<ArgumentException>(
() => OptionStrategies.ShortCallBackspread(canonicalOptionSymbol, strike1, strike2, expiration));
}
[Test]
public void BuildsShortCallBackspreadStrategy()
{
var canonicalOptionSymbol = Symbols.SPY_Option_Chain;
var underlying = Symbols.SPY;
var strike1 = 300m;
var strike2 = 325m;
var expiration = new DateTime(2023, 08, 18);
var strategy = OptionStrategies.ShortCallBackspread(canonicalOptionSymbol, strike1, strike2, expiration);
Assert.AreEqual("Short Call Backspread", strategy.Name);
Assert.AreEqual(underlying, strategy.Underlying);
Assert.AreEqual(canonicalOptionSymbol, strategy.CanonicalOption);
Assert.AreEqual(2, strategy.OptionLegs.Count);
Assert.AreEqual(0, strategy.UnderlyingLegs.Count);
var lowStrikeLeg = strategy.OptionLegs.Single(x => x.Strike == strike1);
Assert.AreEqual(OptionRight.Call, lowStrikeLeg.Right);
Assert.AreEqual(expiration, lowStrikeLeg.Expiration);
Assert.AreEqual(1, lowStrikeLeg.Quantity);
var highStrikeLeg = strategy.OptionLegs.Single(x => x.Strike == strike2);
Assert.AreEqual(OptionRight.Call, highStrikeLeg.Right);
Assert.AreEqual(expiration, highStrikeLeg.Expiration);
Assert.AreEqual(-2, highStrikeLeg.Quantity);
}
[TestCase(325, 350)]
[TestCase(300, 300)]
public void FailsBuildingShortPutBackspreadStrategy(decimal strike1, decimal strike2)
{
var canonicalOptionSymbol = Symbols.SPY_Option_Chain;
var underlying = Symbols.SPY;
var expiration = new DateTime(2023, 08, 18);
// Unordered and repeated strikes
Assert.Throws<ArgumentException>(
() => OptionStrategies.ShortPutBackspread(canonicalOptionSymbol, strike1, strike2, expiration));
}
[Test]
public void BuildsShortPutBackspreadStrategy()
{
var canonicalOptionSymbol = Symbols.SPY_Option_Chain;
var underlying = Symbols.SPY;
var strike1 = 350m;
var strike2 = 300m;
var expiration = new DateTime(2023, 08, 18);
var strategy = OptionStrategies.ShortPutBackspread(canonicalOptionSymbol, strike1, strike2, expiration);
Assert.AreEqual("Short Put Backspread", strategy.Name);
Assert.AreEqual(underlying, strategy.Underlying);
Assert.AreEqual(canonicalOptionSymbol, strategy.CanonicalOption);
Assert.AreEqual(2, strategy.OptionLegs.Count);
Assert.AreEqual(0, strategy.UnderlyingLegs.Count);
var highStrikeLeg = strategy.OptionLegs.Single(x => x.Strike == strike1);
Assert.AreEqual(OptionRight.Put, highStrikeLeg.Right);
Assert.AreEqual(expiration, highStrikeLeg.Expiration);
Assert.AreEqual(1, highStrikeLeg.Quantity);
var lowStrikeLeg = strategy.OptionLegs.Single(x => x.Strike == strike2);
Assert.AreEqual(OptionRight.Put, lowStrikeLeg.Right);
Assert.AreEqual(expiration, lowStrikeLeg.Expiration);
Assert.AreEqual(-2, lowStrikeLeg.Quantity);
}
}
}