Compare commits

..

9 Commits
13503 ... 13541

Author SHA1 Message Date
Martin-Molinero
ca9e55fda6 Add null check for Tradier GetQuotes (#6170)
- Fixing bug where tradier GetQuotes could return null in cases where
  the provided symbol would not match any. Adding unit test.
2022-01-26 19:55:27 -03:00
Martin-Molinero
b698641c90 Minor tweak for ApiDataProvider to support India (#6169)
- Minor tweaks for the ApiDataProvider to better support India market
2022-01-24 21:27:22 -03:00
Ronit Jain
e5c709ee29 Extract zerodha brokerage out of LEAN (#6163)
* Remove zerodha brokerage

* use different brokerage for tests

* remove zerodha files with conflict

* remove redundant dependecies
2022-01-24 18:43:12 -03:00
Martin-Molinero
ca787d0a25 Add support for live price scaling (#6104)
* Add support for live price scaling

- Add support for live trading price scaling for continuous futures.
  Adding unit tests

* Move live price scale application. Updating unit tests

- Live trading application of price scaling will happen before fill
  forwarding and updating securities real time price. Updating unit
  tests to reproduce issue
2022-01-24 18:02:24 -03:00
Martin-Molinero
b1a1277eca Fix GetLastKnownPrices resolution usage (#6165)
- GetLastKnownPrices will no longer guess which resolution to use but
  rely on other methods implementation/
- Updating basic template future algorithms to warmup contracts and
  assert it
- Minor improvements for FunSecurityInitializer and FuncSecuritySeeder
2022-01-21 18:00:53 -03:00
Martin-Molinero
30d7fb042b Always reuse aggregator instance if any (#6161)
* Always reuse aggregator instance if any

- When fetching a IDataAggregator instance from the composer, do not
  enfore type name on existing instances

* Fix unit tests
2022-01-20 18:52:22 -03:00
Ronit Jain
d1bb70fbb7 Add account currency and IRegressionAlgorithmDefinition (#6159)
* Add account currency

* update stats

* use market order, same as c#
2022-01-19 18:25:57 -03:00
Ricardo Andrés Marino Rojas
0946bfc2fb Enable users to use symbol tickers when using Toolkit (#6158)
* If the market ticker has a ":" the user can use the symbol ticker

* Nit change
2022-01-19 15:42:21 -03:00
Ronit Jain
f34be8e3ff Feature add India index algorithms and data (#6145)
* add data

(cherry picked from commit 814011d89e5316d150f88ffca5f48d8d5f0ea7d9)

* update market hours for index

(cherry picked from commit edac40732c120eb84d27de00594b59eebb4983f5)

* add index algorithms

(cherry picked from commit b22d27b4fa98172c435f7c26de4a3a297c49a6b7)

* update statistics

* add cash

* Add india market

* add leverage for index

* can subscribe to index

* update format

* fix wrong cash

* fix ticker names

* update data

* update ticker and stats

* update docs
2022-01-19 12:14:48 -03:00
64 changed files with 905 additions and 5636 deletions

View File

@@ -18,6 +18,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
using QuantConnect.Securities.Future;
@@ -64,6 +65,9 @@ namespace QuantConnect.Algorithm.CSharp
var benchmark = AddEquity("SPY");
SetBenchmark(benchmark.Symbol);
var seeder = new FuncSecuritySeeder(GetLastKnownPrices);
SetSecurityInitializer(security => seeder.SeedSecurity(security));
}
/// <summary>
@@ -112,6 +116,19 @@ namespace QuantConnect.Algorithm.CSharp
var maintenanceIntraday = futureMarginModel.MaintenanceIntradayMarginRequirement;
}
public override void OnSecuritiesChanged(SecurityChanges changes)
{
foreach (var addedSecurity in changes.AddedSecurities)
{
if (addedSecurity.Symbol.SecurityType == SecurityType.Future
&& !addedSecurity.Symbol.IsCanonical()
&& !addedSecurity.HasData)
{
throw new Exception($"Future contracts did not work up as expected: {addedSecurity.Symbol}");
}
}
}
/// <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>

View File

@@ -14,8 +14,9 @@
*/
using QuantConnect.Data;
using QuantConnect.Orders;
using System.Collections.Generic;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
namespace QuantConnect.Algorithm.CSharp
{
@@ -26,16 +27,17 @@ namespace QuantConnect.Algorithm.CSharp
/// <meta name="tag" content="using data" />
/// <meta name="tag" content="using quantconnect" />
/// <meta name="tag" content="trading and orders" />
public class BasicTemplateIndiaAlgorithm : QCAlgorithm
public class BasicTemplateIndiaAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
/// <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()
{
SetAccountCurrency("INR"); //Set Account Currency
SetStartDate(2019, 1, 23); //Set Start Date
SetEndDate(2019, 10, 31); //Set End Date
SetCash(100000); //Set Strategy Cash
SetEndDate(2019, 10, 31); //Set End Date
SetCash(100000); //Set Strategy Cash
// Find more symbols here: http://quantconnect.com/data
// Equities Resolutions: Tick, Second, Minute, Hour, Daily.
@@ -85,48 +87,48 @@ namespace QuantConnect.Algorithm.CSharp
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "3"},
{"Total Trades", "1"},
{"Average Win", "0%"},
{"Average Loss", "-1.01%"},
{"Compounding Annual Return", "261.134%"},
{"Drawdown", "2.200%"},
{"Expectancy", "-1"},
{"Net Profit", "1.655%"},
{"Sharpe Ratio", "8.505"},
{"Probabilistic Sharpe Ratio", "66.840%"},
{"Loss Rate", "100%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "-0.010%"},
{"Drawdown", "0.000%"},
{"Expectancy", "0"},
{"Net Profit", "-0.008%"},
{"Sharpe Ratio", "-1.183"},
{"Probabilistic Sharpe Ratio", "0.001%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "-0.091"},
{"Beta", "1.006"},
{"Annual Standard Deviation", "0.224"},
{"Annual Variance", "0.05"},
{"Information Ratio", "-33.445"},
{"Tracking Error", "0.002"},
{"Treynor Ratio", "1.893"},
{"Total Fees", "$10.32"},
{"Estimated Strategy Capacity", "$27000000.00"},
{"Lowest Capacity Asset", "SPY R735QTJ8XC9X"},
{"Fitness Score", "0.747"},
{"Kelly Criterion Estimate", "38.796"},
{"Kelly Criterion Probability Value", "0.228"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "85.095"},
{"Portfolio Turnover", "0.747"},
{"Total Insights Generated", "100"},
{"Total Insights Closed", "99"},
{"Total Insights Analysis Completed", "99"},
{"Long Insight Count", "100"},
{"Alpha", "0"},
{"Beta", "0"},
{"Annual Standard Deviation", "0"},
{"Annual Variance", "0"},
{"Information Ratio", "-1.183"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$6.00"},
{"Estimated Strategy Capacity", "$61000000000.00"},
{"Lowest Capacity Asset", "YESBANK UL"},
{"Fitness Score", "0"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "-0.247"},
{"Return Over Maximum Drawdown", "-1.104"},
{"Portfolio Turnover", "0"},
{"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", "$135639.1761"},
{"Total Accumulated Estimated Alpha Value", "$21852.9784"},
{"Mean Population Estimated Insight Value", "$220.7372"},
{"Mean Population Direction", "53.5354%"},
{"Mean Population Magnitude", "53.5354%"},
{"Rolling Averaged Population Direction", "58.2788%"},
{"Rolling Averaged Population Magnitude", "58.2788%"},
{"OrderListHash", "ad2216297c759d8e5aef48ff065f8919"}
{"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", "6cc69218edd7bd461678b9ee0c575db5"}
};
}
}

View File

@@ -0,0 +1,158 @@
/*
* 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.Data;
using System.Collections.Generic;
using QuantConnect.Indicators;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This example demonstrates how to add index asset types.
/// </summary>
/// <meta name="tag" content="using data" />
/// <meta name="tag" content="benchmarks" />
/// <meta name="tag" content="indexes" />
public class BasicTemplateIndiaIndexAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
protected Symbol Nifty;
protected Symbol NiftyETF;
private ExponentialMovingAverage _emaSlow;
private ExponentialMovingAverage _emaFast;
/// <summary>
/// Initialize your algorithm and add desired assets.
/// </summary>
public override void Initialize()
{
SetAccountCurrency("INR"); //Set Account Currency
SetStartDate(2019, 1, 1); //Set End Date
SetEndDate(2019, 1, 5); //Set End Date
SetCash(1000000); //Set Strategy Cash
// Use indicator for signal; but it cannot be traded
Nifty = AddIndex("NIFTY50", Resolution.Minute, Market.India).Symbol;
//Trade Index based ETF
NiftyETF = AddEquity("JUNIORBEES", Resolution.Minute, Market.India).Symbol;
//Set Order Prperties as per the requirements for order placement
DefaultOrderProperties = new IndiaOrderProperties(exchange: Exchange.NSE);
_emaSlow = EMA(Nifty, 80);
_emaFast = EMA(Nifty, 200);
}
/// <summary>
/// Index EMA Cross trading underlying.
/// </summary>
public override void OnData(Slice slice)
{
if (!slice.Bars.ContainsKey(Nifty) || !slice.Bars.ContainsKey(NiftyETF))
{
return;
}
// Warm up indicators
if (!_emaSlow.IsReady)
{
return;
}
if (_emaFast > _emaSlow)
{
if (!Portfolio.Invested)
{
var marketTicket = MarketOrder(NiftyETF, 1);
}
}
else
{
Liquidate();
}
}
public override void OnEndOfAlgorithm()
{
if (Portfolio[Nifty].TotalSaleVolume > 0)
{
throw new Exception("Index is not tradable.");
}
}
/// <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 virtual bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public virtual 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 virtual Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "6"},
{"Average Win", "0%"},
{"Average Loss", "0.00%"},
{"Compounding Annual Return", "-0.395%"},
{"Drawdown", "0.000%"},
{"Expectancy", "-1"},
{"Net Profit", "-0.004%"},
{"Sharpe Ratio", "-23.595"},
{"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", "-23.595"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$36.00"},
{"Estimated Strategy Capacity", "$74000.00"},
{"Lowest Capacity Asset", "JUNIORBEES UL"},
{"Fitness Score", "0"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "-29.6"},
{"Return Over Maximum Drawdown", "-123.624"},
{"Portfolio Turnover", "0"},
{"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", "4637f26543287548b28a3c296db055d3"}
};
}
}

View File

@@ -45,6 +45,7 @@ namespace QuantConnect.Algorithm.CSharp
/// </summary>
public override void Initialize()
{
SetAccountCurrency("INR"); //Set Account Currency
SetStartDate(2004, 5, 20); //Set Start Date
SetEndDate(2016, 7, 26); //Set End Date
_mappingSymbol = AddEquity("3MINDIA", Resolution.Daily, Market.India).Symbol;
@@ -170,8 +171,8 @@ namespace QuantConnect.Algorithm.CSharp
{"Beta", "0"},
{"Annual Standard Deviation", "0"},
{"Annual Variance", "0"},
{"Information Ratio", "-0.427"},
{"Tracking Error", "0.158"},
{"Information Ratio", "0"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$0.00"},
{"Estimated Strategy Capacity", "$0"},
@@ -188,9 +189,9 @@ namespace QuantConnect.Algorithm.CSharp
{"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"},
{"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%"},

View File

@@ -43,6 +43,8 @@ class BasicTemplateFuturesAlgorithm(QCAlgorithm):
benchmark = self.AddEquity("SPY")
self.SetBenchmark(benchmark.Symbol)
seeder = FuncSecuritySeeder(self.GetLastKnownPrices)
self.SetSecurityInitializer(lambda security: seeder.SeedSecurity(security))
def OnData(self,slice):
if not self.Portfolio.Invested:
@@ -70,3 +72,8 @@ class BasicTemplateFuturesAlgorithm(QCAlgorithm):
maintenanceOvernight = buyingPowerModel.MaintenanceOvernightMarginRequirement
initialIntraday = buyingPowerModel.InitialIntradayMarginRequirement
maintenanceIntraday = buyingPowerModel.MaintenanceIntradayMarginRequirement
def OnSecuritiesChanged(self, changes):
for addedSecurity in changes.AddedSecurities:
if addedSecurity.Symbol.SecurityType == SecurityType.Future and not addedSecurity.Symbol.IsCanonical() and not addedSecurity.HasData:
raise Exception(f"Future contracts did not work up as expected: {addedSecurity.Symbol}")

View File

@@ -25,9 +25,10 @@ class BasicTemplateIndiaAlgorithm(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.SetAccountCurrency("INR") #Set Account Currency
self.SetStartDate(2019, 1, 23) #Set Start Date
self.SetEndDate(2019, 10, 31) #Set End Date
self.SetCash(100000) #Set Strategy Cash
self.SetEndDate(2019, 10, 31) #Set End Date
self.SetCash(100000) #Set Strategy Cash
# Find more symbols here: http://quantconnect.com/data
self.AddEquity("YESBANK", Resolution.Minute, Market.India)
self.Debug("numpy test >>> print numpy.pi: " + str(np.pi))
@@ -42,7 +43,7 @@ class BasicTemplateIndiaAlgorithm(QCAlgorithm):
data: Slice object keyed by symbol containing the stock data
'''
if not self.Portfolio.Invested:
self.SetHoldings("YESBANK", 1)
self.MarketOrder("YESBANK", 1)
def OnOrderEvent(self, orderEvent):
if orderEvent.Status == OrderStatus.Filled:

View File

@@ -0,0 +1,71 @@
# 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 AlgorithmImports import *
### <summary>
### Basic Template India Index Algorithm uses framework components to define the algorithm.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="using quantconnect" />
### <meta name="tag" content="trading and orders" />
class BasicTemplateIndiaIndexAlgorithm(QCAlgorithm):
'''Basic template framework algorithm uses framework components to define the algorithm.'''
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.SetAccountCurrency("INR") #Set Account Currency
self.SetStartDate(2019, 1, 1) #Set Start Date
self.SetEndDate(2019, 1, 5) #Set End Date
self.SetCash(1000000) #Set Strategy Cash
# Use indicator for signal; but it cannot be traded
self.Nifty = self.AddIndex("NIFTY50", Resolution.Minute, Market.India).Symbol
# Trade Index based ETF
self.NiftyETF = self.AddEquity("JUNIORBEES", Resolution.Minute, Market.India).Symbol
# Set Order Prperties as per the requirements for order placement
self.DefaultOrderProperties = IndiaOrderProperties(Exchange.NSE)
# Define indicator
self._emaSlow = self.EMA(self.Nifty, 80);
self._emaFast = self.EMA(self.Nifty, 200);
self.Debug("numpy test >>> print numpy.pi: " + str(np.pi))
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 not data.Bars.ContainsKey(self.Nifty) or not data.Bars.ContainsKey(self.NiftyETF):
return
if not self._emaSlow.IsReady:
return
if self._emaFast > self._emaSlow:
if not self.Portfolio.Invested:
self.marketTicket = self.MarketOrder(self.NiftyETF, 1)
else:
self.Liquidate()
def OnEndOfAlgorithm(self):
if self.Portfolio[self.Nifty].TotalSaleVolume > 0:
raise Exception("Index is not tradable.")

View File

@@ -24,6 +24,8 @@ class IndiaDataRegressionAlgorithm(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.SetAccountCurrency("INR")
self.SetStartDate(2004, 5, 20)
self.SetEndDate(2016, 7, 26)
self._mappingSymbol = self.AddEquity("3MINDIA", Resolution.Daily, Market.India).Symbol

View File

@@ -513,16 +513,18 @@ namespace QuantConnect.Algorithm
}
var result = new Dictionary<TickType, BaseData>();
// For speed and memory usage, use Resolution.Minute as the minimum resolution
var resolution = (Resolution)Math.Max((int)Resolution.Minute,
(int)SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(symbol).GetHighestResolution());
Resolution? resolution = null;
Func<int, bool> requestData = period =>
{
var historyRequests = CreateBarCountHistoryRequests(new[] { symbol }, period, resolution)
var historyRequests = CreateBarCountHistoryRequests(new[] { symbol }, period)
.Select(request =>
{
// For speed and memory usage, use Resolution.Minute as the minimum resolution
request.Resolution = (Resolution)Math.Max((int)Resolution.Minute, (int)request.Resolution);
// force no fill forward behavior
request.FillForwardResolution = null;
resolution = request.Resolution;
return request;
})
// request only those tick types we didn't get the data we wanted
@@ -547,12 +549,21 @@ namespace QuantConnect.Algorithm
if (!requestData(5))
{
// If the first attempt to get the last know price returns null, it maybe the case of an illiquid security.
// We increase the look-back period for this case accordingly to the resolution to cover 3 trading days
var periods =
resolution == Resolution.Daily ? 3 :
resolution == Resolution.Hour ? 24 : 1440;
requestData(periods);
if (resolution.HasValue)
{
// If the first attempt to get the last know price returns null, it maybe the case of an illiquid security.
// We increase the look-back period for this case accordingly to the resolution to cover 3 trading days
var periods =
resolution.Value == Resolution.Daily ? 3 :
resolution.Value == Resolution.Hour ? 24 : 1440;
requestData(periods);
}
else
{
// this shouldn't happen but just in case
QuantConnect.Logging.Log.Error(
$"QCAlgorithm.GetLastKnownPrices(): no history request was created for symbol {symbol} at {Time}");
}
}
// return the data ordered by time ascending
return result.Values.OrderBy(data => data.Time);

View File

@@ -320,7 +320,7 @@ namespace QuantConnect.Brokerages.Binance
var apiKey = job.BrokerageData["binance-api-key"];
var apiSecret = job.BrokerageData["binance-api-secret"];
var aggregator = Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(
Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"));
Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false);
Initialize(
wssUrl: webSocketBaseUrl,

View File

@@ -88,7 +88,7 @@ namespace QuantConnect.Brokerages.Binance
job.BrokerageData["binance-api-url"],
job.BrokerageData["binance-websocket-url"],
algorithm,
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager")),
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false),
job);
Composer.Instance.AddPart<IDataQueueHandler>(brokerage);

View File

@@ -443,7 +443,7 @@ namespace QuantConnect.Brokerages.Bitfinex
var apiKey = job.BrokerageData["bitfinex-api-key"];
var apiSecret = job.BrokerageData["bitfinex-api-secret"];
var aggregator = Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(
Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"));
Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false);
Initialize(
wssUrl: WebSocketUrl,

View File

@@ -84,7 +84,7 @@ namespace QuantConnect.Brokerages.Bitfinex
job.BrokerageData["bitfinex-api-secret"],
algorithm,
priceProvider,
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager")),
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false),
job);
Composer.Instance.AddPart<IDataQueueHandler>(brokerage);

View File

@@ -91,7 +91,7 @@ namespace QuantConnect.Brokerages.GDAX
var restClient = new RestClient(restApi);
var webSocketClient = new WebSocketClientWrapper();
var priceProvider = new ApiPriceProvider(job.UserId, job.UserToken);
var aggregator = Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"));
var aggregator = Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false);
IBrokerage brokerage;
if (job.DataQueueHandler.Contains("GDAXDataQueueHandler"))

View File

@@ -97,7 +97,7 @@ namespace QuantConnect.Brokerages.GDAX
var apiSecret = job.BrokerageData["gdax-api-secret"];
var priceProvider = new ApiPriceProvider(job.UserId, job.UserToken);
var aggregator = Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(
Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"));
Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false);
Initialize(
wssUrl: wssUrl,

View File

@@ -2614,7 +2614,7 @@ namespace QuantConnect.Brokerages.InteractiveBrokers
Initialize(null,
null,
null,
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager")),
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false),
Composer.Instance.GetExportedValueByTypeName<IMapFileProvider>(Config.Get("map-file-provider", "QuantConnect.Data.Auxiliary.LocalDiskMapFileProvider")),
account,
host,

View File

@@ -102,7 +102,7 @@ namespace QuantConnect.Brokerages.InteractiveBrokers
algorithm,
algorithm.Transactions,
algorithm.Portfolio,
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager")),
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false),
Composer.Instance.GetExportedValueByTypeName<IMapFileProvider>(Config.Get("map-file-provider", "QuantConnect.Data.Auxiliary.LocalDiskMapFileProvider")),
account,
host,

View File

@@ -261,7 +261,7 @@ namespace QuantConnect.Brokerages.Oanda
Initialize(
null,
null,
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager")),
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false),
environment,
accessToken,
accountId,

View File

@@ -96,7 +96,7 @@ namespace QuantConnect.Brokerages.Oanda
var brokerage = new OandaBrokerage(
algorithm.Transactions,
algorithm.Portfolio,
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager")),
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false),
environment,
accessToken,
accountId,

View File

@@ -37,7 +37,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NodaTime" Version="3.0.5" />
<PackageReference Include="QuantConnect.IBAutomater" Version="2.0.64" />
<PackageReference Include="RestSharp" Version="106.12.0" />
</ItemGroup>

View File

@@ -52,7 +52,7 @@ namespace QuantConnect.Brokerages.Tradier
var useSandbox = bool.Parse(job.BrokerageData["tradier-use-sandbox"]);
var accountId = job.BrokerageData["tradier-account-id"];
var accessToken = job.BrokerageData["tradier-access-token"];
var aggregator = Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"));
var aggregator = Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false);
Initialize(
wssUrl: WebSocketUrl,

View File

@@ -468,7 +468,8 @@ namespace QuantConnect.Brokerages.Tradier
request.AddParameter("symbols", csvSymbols, ParameterType.QueryString);
var dataContainer = Execute<TradierQuoteContainer>(request, TradierApiRequestType.Data, "quotes");
return dataContainer.Quotes;
// can return null quotes and not really be failing for cases where the provided symbols do not match
return dataContainer.Quotes ?? new List<TradierQuote>();
}
/// <summary>

View File

@@ -101,7 +101,7 @@ namespace QuantConnect.Brokerages.Tradier
algorithm,
algorithm.Transactions,
algorithm.Portfolio,
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager")),
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false),
useSandbox,
accountId,
accessToken);

View File

@@ -1,120 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Brokerages.Zerodha
{
#pragma warning disable 1591
/// <summary>
/// Types of product supported by Kite
/// </summary>
public enum KiteProductType
{
MIS,
CNC,
NRML
}
/// <summary>
/// Types of order supported by Kite
/// </summary>
public enum KiteOrderType
{
MARKET,
LIMIT,
SLM,
SL
}
public class Constants
{
// Products
public const string PRODUCT_MIS = "MIS";
public const string PRODUCT_CNC = "CNC";
public const string PRODUCT_NRML = "NRML";
// Order types
public const string ORDER_TYPE_MARKET = "MARKET";
public const string ORDER_TYPE_LIMIT = "LIMIT";
public const string ORDER_TYPE_SLM = "SL-M";
public const string ORDER_TYPE_SL = "SL";
// Order status
public const string ORDER_STATUS_COMPLETE = "COMPLETE";
public const string ORDER_STATUS_CANCELLED = "CANCELLED";
public const string ORDER_STATUS_REJECTED = "REJECTED";
// Varities
public const string VARIETY_REGULAR = "regular";
public const string VARIETY_BO = "bo";
public const string VARIETY_CO = "co";
public const string VARIETY_AMO = "amo";
// Transaction type
public const string TRANSACTION_TYPE_BUY = "BUY";
public const string TRANSACTION_TYPE_SELL = "SELL";
// Validity
public const string VALIDITY_DAY = "DAY";
public const string VALIDITY_IOC = "IOC";
// Exchanges
public const string EXCHANGE_NSE = "NSE";
public const string EXCHANGE_BSE = "BSE";
public const string EXCHANGE_NFO = "NFO";
public const string EXCHANGE_CDS = "CDS";
public const string EXCHANGE_BFO = "BFO";
public const string EXCHANGE_MCX = "MCX";
// Margins segments
public const string MARGIN_EQUITY = "equity";
public const string MARGIN_COMMODITY = "commodity";
// Ticker modes
public const string MODE_FULL = "full";
public const string MODE_QUOTE = "quote";
public const string MODE_LTP = "ltp";
// Positions
public const string POSITION_DAY = "day";
public const string POSITION_OVERNIGHT = "overnight";
// Historical intervals
public const string INTERVAL_MINUTE = "minute";
public const string INTERVAL_3MINUTE = "3minute";
public const string INTERVAL_5MINUTE = "5minute";
public const string INTERVAL_10MINUTE = "10minute";
public const string INTERVAL_15MINUTE = "15minute";
public const string INTERVAL_30MINUTE = "30minute";
public const string INTERVAL_60MINUTE = "60minute";
public const string INTERVAL_DAY = "day";
// GTT status
public const string GTT_ACTIVE = "active";
public const string GTT_TRIGGERED = "triggered";
public const string GTT_DISABLED = "disabled";
public const string GTT_EXPIRED = "expired";
public const string GTT_CANCELLED = "cancelled";
public const string GTT_REJECTED = "rejected";
public const string GTT_DELETED = "deleted";
// GTT trigger type
public const string GTT_TRIGGER_OCO = "two-leg";
public const string GTT_TRIGGER_SINGLE = "single";
}
#pragma warning restore 1591
}

View File

@@ -1,60 +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;
namespace QuantConnect.Brokerages.Zerodha
{
internal static partial class ExceptionExtensions
{
/// <summary>
/// Returns a list of all the exception messages from the top-level
/// exception down through all the inner exceptions. Useful for making
/// logs and error pages easier to read when dealing with exceptions.
/// Usage: Exception.Messages()
/// </summary>
public static IEnumerable<string> Messages(this Exception ex)
{
// return an empty sequence if the provided exception is null
if (ex == null) { yield break; }
// first return THIS exception's message at the beginning of the list
yield return ex.Message;
// then get all the lower-level exception messages recursively (if any)
IEnumerable<Exception> innerExceptions = Enumerable.Empty<Exception>();
if (ex is AggregateException && (ex as AggregateException).InnerExceptions.Any())
{
innerExceptions = (ex as AggregateException).InnerExceptions;
}
else if (ex.InnerException != null)
{
innerExceptions = new Exception[] { ex.InnerException };
}
foreach (var innerEx in innerExceptions)
{
foreach (string msg in innerEx.Messages())
{
yield return msg;
}
}
}
}
}

View File

@@ -1,87 +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.Net;
namespace QuantConnect.Brokerages.Zerodha
{
#pragma warning disable 1591
/// <summary>
/// KiteAPI Exceptions
/// </summary>
public class KiteException : Exception
{
HttpStatusCode status;
public KiteException(string message, HttpStatusCode httpStatus, Exception innerException = null) : base(message, innerException) { status = httpStatus; }
}
/// <summary>
/// General Exceptions
/// </summary>
public class GeneralException : KiteException
{
public GeneralException(string message, HttpStatusCode httpStatus = HttpStatusCode.InternalServerError, Exception innerException = null) : base(message, httpStatus, innerException) { }
}
/// <summary>
/// Token Exceptions
/// </summary>
public class TokenException : KiteException
{
public TokenException(string message, HttpStatusCode httpStatus = HttpStatusCode.Forbidden, Exception innerException = null) : base(message, httpStatus, innerException) { }
}
/// <summary>
/// Permission Exceptions
/// </summary>
public class PermissionException : KiteException
{
public PermissionException(string message, HttpStatusCode httpStatus = HttpStatusCode.Forbidden, Exception innerException = null) : base(message, httpStatus, innerException) { }
}
/// <summary>
/// Order Exceptions
/// </summary>
public class OrderException : KiteException
{
public OrderException(string message, HttpStatusCode httpStatus = HttpStatusCode.BadRequest, Exception innerException = null) : base(message, httpStatus, innerException) { }
}
/// <summary>
/// InputExceptions
/// </summary>
public class InputException : KiteException
{
public InputException(string message, HttpStatusCode httpStatus = HttpStatusCode.BadRequest, Exception innerException = null) : base(message, httpStatus, innerException) { }
}
/// <summary>
/// DataExceptions
/// </summary>
public class DataException : KiteException
{
public DataException(string message, HttpStatusCode httpStatus = HttpStatusCode.BadGateway, Exception innerException = null) : base(message, httpStatus, innerException) { }
}
/// <summary>
/// Network Exceptions
/// </summary>
public class NetworkException : KiteException
{
public NetworkException(string message, HttpStatusCode httpStatus = HttpStatusCode.ServiceUnavailable, Exception innerException = null) : base(message, httpStatus, innerException) { }
}
#pragma warning restore 1591
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,189 +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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using System.Globalization;
using Newtonsoft.Json.Linq;
namespace QuantConnect.Brokerages.Zerodha
{
/// <summary>
/// Zerodha utility class
/// </summary>
public class Utils
{
/// <summary>
/// Convert string to Date object
/// </summary>
/// <param name="dateString">Date string.</param>
/// <returns>Date object/</returns>
public static DateTime? StringToDate(string dateString)
{
if (dateString == null)
{
return null;
}
try
{
return DateTime.ParseExact(dateString, dateString.Length == 10 ? "yyyy-MM-dd" : "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// Serialize C# object to JSON string.
/// </summary>
/// <param name="obj">C# object to serialize.</param>
/// <returns>JSON string/</returns>
public static string JsonSerialize(object obj)
{
string json = JsonConvert.SerializeObject(obj);
MatchCollection mc = Regex.Matches(json, @"\\/Date\((\d*?)\)\\/");
foreach (Match m in mc)
{
var unix = Convert.ToInt64(m.Groups[1].Value,CultureInfo.InvariantCulture) / 1000;
json = json.Replace(m.Groups[0].Value, UnixToDateTime(unix).ToStringInvariant());
}
return json;
}
/// <summary>
/// Deserialize Json string to nested string dictionary.
/// </summary>
/// <param name="Json">Json string to deserialize.</param>
/// <returns>Json in the form of nested string dictionary.</returns>
public static JObject JsonDeserialize(string Json)
{
JObject jObject = JObject.Parse(Json);
return jObject;
}
/// <summary>
/// Recursively traverses an object and converts double fields to decimal.
/// This is used in Json deserialization. JavaScriptSerializer converts floats
/// in exponential notation to double and everthing else to double. This function
/// makes everything decimal. Currently supports only Dictionary and Array as input.
/// </summary>
/// <param name="obj">Input object.</param>
/// <returns>Object with decimals instead of doubles</returns>
public static dynamic DoubleToDecimal(dynamic obj)
{
if (obj is double)
{
obj = Convert.ToDecimal(obj);
}
else if (obj is IDictionary)
{
var keys = new List<string>(obj.Keys);
for (int i = 0; i < keys.Count; i++)
{
obj[keys[i]] = DoubleToDecimal(obj[keys[i]]);
}
}
else if (obj is ICollection)
{
obj = new ArrayList(obj);
for (int i = 0; i < obj.Count; i++)
{
obj[i] = DoubleToDecimal(obj[i]);
}
}
return obj;
}
/// <summary>
/// Wraps a string inside a stream
/// </summary>
/// <param name="value">string data</param>
/// <returns>Stream that reads input string</returns>
public static MemoryStream StreamFromString(string value)
{
return new MemoryStream(Encoding.UTF8.GetBytes(value ?? ""));
}
/// <summary>
/// Helper function to add parameter to the request only if it is not null or empty
/// </summary>
/// <param name="Params">Dictionary to add the key-value pair</param>
/// <param name="Key">Key of the parameter</param>
/// <param name="Value">Value of the parameter</param>
public static void AddIfNotNull(Dictionary<string, dynamic> Params, string Key, string Value)
{
if (!String.IsNullOrEmpty(Value))
Params.Add(Key, Value);
}
/// <summary>
/// Creates key=value with url encoded value
/// </summary>
/// <param name="Key">Key</param>
/// <param name="Value">Value</param>
/// <returns>Combined string</returns>
public static string BuildParam(string Key, dynamic Value)
{
if (Value is string)
{
return HttpUtility.UrlEncode(Key) + "=" + HttpUtility.UrlEncode((string)Value);
}
else
{
string[] values = (string[])Value;
return String.Join("&", values.Select(x => HttpUtility.UrlEncode(Key) + "=" + HttpUtility.UrlEncode(x)));
}
}
/// <summary>
/// Convert Unix TimeStamp to DateTime
/// </summary>
/// <param name="unixTimeStamp">Timestamp to convert</param>
/// <returns><see cref="DateTime"/> object representing the timestamp</returns>
public static DateTime UnixToDateTime(long unixTimeStamp)
{
// Unix timestamp is seconds past epoch
DateTime dateTime = new DateTime(1970, 1, 1, 5, 30, 0, 0, DateTimeKind.Unspecified);
dateTime = dateTime.AddSeconds(unixTimeStamp);
return dateTime;
}
/// <summary>
/// Convert ArrayList to list of <see cref="decimal"/>
/// </summary>
/// <param name="arrayList"><see cref="ArrayList"/> to convert</param>
/// <returns>List of <see cref="decimal"/>s</returns>
public static List<decimal> ToDecimalList(ArrayList arrayList)
{
var res = new List<decimal>();
foreach(var i in arrayList)
{
res.Add(Convert.ToDecimal(i,CultureInfo.InvariantCulture));
}
return res;
}
}
}

View File

@@ -1,102 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using QuantConnect.Configuration;
using QuantConnect.Data;
using QuantConnect.Packets;
using QuantConnect.Util;
using System;
using System.Collections.Generic;
namespace QuantConnect.Brokerages.Zerodha
{
/// <summary>
/// ZerodhaBrokerage: IDataQueueHandler implementation
/// </summary>
public partial class ZerodhaBrokerage
{
#region IDataQueueHandler implementation
/// <summary>
/// Sets the job we're subscribing for
/// </summary>
/// <param name="job">Job we're subscribing for</param>
public void SetJob(LiveNodePacket job)
{
Initialize(
job.BrokerageData["zerodha-trading-segment"],
job.BrokerageData["zerodha-product-type"],
job.BrokerageData["zerodha-api-key"],
job.BrokerageData["zerodha-access-token"],
null,
null,
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"))
);
if (!IsConnected)
{
Connect();
}
}
/// <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)
{
var symbol = dataConfig.Symbol;
if (!CanSubscribe(symbol))
{
return null;
}
var enumerator = _aggregator.Add(dataConfig, newDataAvailableHandler);
SubscriptionManager.Subscribe(dataConfig);
return enumerator;
}
/// <summary>
/// UnSubscribe to the specified configuration
/// </summary>
/// <param name="dataConfig">defines the parameters to subscribe to a data feed</param>
public void Unsubscribe(SubscriptionDataConfig dataConfig)
{
SubscriptionManager.Unsubscribe(dataConfig);
_aggregator.Remove(dataConfig);
}
/// <summary>
/// Returns true if this data provide can handle the specified symbol
/// </summary>
/// <param name="symbol">The symbol to be handled</param>
/// <returns>True if this data provider can get data for the symbol, false otherwise</returns>
private static bool CanSubscribe(Symbol symbol)
{
var market = symbol.ID.Market;
var securityType = symbol.ID.SecurityType;
if (symbol.Value.IndexOfInvariant("universe", true) != -1) return false;
// Include future options as a special case with no matching market, otherwise
// our subscriptions are removed without any sort of notice.
return
(securityType == SecurityType.Equity) && (market == Market.India);
}
#endregion IDataQueueHandler implementation
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,92 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using QuantConnect.Configuration;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
using QuantConnect.Util;
namespace QuantConnect.Brokerages.Zerodha
{
/// <summary>
/// Factory method to create Zerodha Websockets brokerage
/// </summary>
public class ZerodhaBrokerageFactory : BrokerageFactory
{
/// <summary>
/// Factory constructor
/// </summary>
public ZerodhaBrokerageFactory() : base(typeof(ZerodhaBrokerage))
{
}
/// <summary>
/// Not required
/// </summary>
public override void Dispose()
{
}
/// <summary>
/// provides brokerage connection data
/// </summary>
public override Dictionary<string, string> BrokerageData => new Dictionary<string, string>
{
{ "zerodha-api-key", Config.Get("zerodha-api-key")},
{ "zerodha-access-token", Config.Get("zerodha-access-token")},
{ "zerodha-trading-segment", Config.Get("zerodha-trading-segment")},
{ "zerodha-product-type", Config.Get("zerodha-product-type")},
};
/// <summary>
/// The brokerage model
/// </summary>
/// <param name="orderProvider">The order provider</param>
public override IBrokerageModel GetBrokerageModel(IOrderProvider orderProvider) => new ZerodhaBrokerageModel();
/// <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[] { "zerodha-api-key", "zerodha-access-token", "zerodha-trading-segment"};
foreach (var item in required)
{
if (string.IsNullOrEmpty(job.BrokerageData[item]))
throw new Exception($"ZerodhaBrokerageFactory.CreateBrokerage: Missing {item} in config.json");
}
var brokerage = new ZerodhaBrokerage(
job.BrokerageData["zerodha-trading-segment"],
job.BrokerageData["zerodha-product-type"],
job.BrokerageData["zerodha-api-key"],
job.BrokerageData["zerodha-access-token"],
algorithm,
algorithm.Portfolio,
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"))
);
//Add the brokerage to the composer to ensure its accessible to the live data feed.
Composer.Instance.AddPart<IDataQueueHandler>(brokerage);
return brokerage;
}
}
}

View File

@@ -1,393 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using QuantConnect.Brokerages.Zerodha.Messages;
using QuantConnect.Util;
namespace QuantConnect.Brokerages.Zerodha
{
/// <summary>
/// Provides the mapping between Lean symbols and Zerodha symbols.
/// </summary>
public class ZerodhaSymbolMapper : ISymbolMapper
{
/// <summary>
/// Symbols that are Tradable
/// </summary>
public List<Symbol> KnownSymbols
{
get
{
return KnownSymbolsList;
}
}
/// <summary>
/// Custom class to store information about symbols
/// </summary>
private class SymbolData
{
/// <summary>
/// Stores exchange name for the tradingSymbol
/// </summary>
public string Exchange { get; set;}
/// <summary>
/// Stores instrumentToken name for the tradingSymbol
/// </summary>
public uint InstrumentToken {get; set;}
/// <summary>
/// Initalize values to the class attributes
/// </summary>
public SymbolData(uint token, string exchangeName)
{
Exchange = exchangeName;
InstrumentToken = token;
}
}
/// <summary>
/// The list of known Zerodha symbols.
/// </summary>
private List<Symbol> KnownSymbolsList = new List<Symbol>();
/// <summary>
/// Mapping between brokerageSymbol and a list of all available SymbolData objects for the brokerageSymbol.
/// </summary>
private Dictionary<string, List<SymbolData>> ZerodhaInstrumentsList = new Dictionary<string, List<SymbolData>>();
/// <summary>
/// Mapping between instrumentToken and it's market segment ( E.g: 408065-> nse)
/// </summary>
private Dictionary<uint,string> ZerodhaInstrumentsExchangeMapping = new Dictionary<uint,string>();
/// <summary>
/// Constructs default instance of the Zerodha Sybol Mapper
/// </summary>
public ZerodhaSymbolMapper(Kite kite, string exchange = "")
{
KnownSymbolsList = GetTradableInstrumentsList(kite, exchange);
}
/// <summary>
/// Get list of tradable symbol
/// </summary>
/// <param name="kite">Kite</param>
/// <param name="exchange">Exchange</param>
/// <returns></returns>
private List<Symbol> GetTradableInstrumentsList(Kite kite, string exchange = "")
{
var tradableInstruments = kite.GetInstruments(exchange);
var symbols = new List<Symbol>();
var zerodhaInstrumentsMapping = new Dictionary<string, List<SymbolData>>();
var zerodhaTokenExchangeDict = new Dictionary<uint,string>();
foreach (var tp in tradableInstruments)
{
var securityType = SecurityType.Equity;
var market = Market.India;
zerodhaTokenExchangeDict[tp.InstrumentToken] = tp.Exchange.ToLowerInvariant();
OptionRight optionRight = 0;
switch (tp.InstrumentType)
{
//Equities
case "EQ":
securityType = SecurityType.Equity;
break;
//Call Options
case "CE":
securityType = SecurityType.Option;
optionRight = OptionRight.Call;
break;
//Put Options
case "PE":
securityType = SecurityType.Option;
optionRight = OptionRight.Put;
break;
//Stock Futures
case "FUT":
securityType = SecurityType.Future;
break;
default:
securityType = SecurityType.Base;
break;
}
if (securityType == SecurityType.Option)
{
var strikePrice = tp.Strike;
var expiryDate = tp.Expiry;
//TODO: Handle parsing of BCDOPT strike price
if(tp.Segment!= "BCD-OPT")
{
var symbol = GetLeanSymbol(tp.Name.Trim().Replace(" ", ""), securityType, market, (DateTime)expiryDate, GetStrikePrice(tp), optionRight);
symbols.Add(symbol);
var cleanSymbol = tp.TradingSymbol.Trim().Replace(" ", "");
if (!zerodhaInstrumentsMapping.ContainsKey(cleanSymbol))
{
zerodhaInstrumentsMapping[cleanSymbol] = new List<SymbolData>();
}
zerodhaInstrumentsMapping[cleanSymbol].Add(new SymbolData(tp.InstrumentToken,market));
}
}
if (securityType == SecurityType.Future)
{
var expiryDate = tp.Expiry;
var cleanSymbol = tp.TradingSymbol.Trim().Replace(" ", "");
var symbol = GetLeanSymbol(cleanSymbol, securityType, market, (DateTime)expiryDate);
symbols.Add(symbol);
if (!zerodhaInstrumentsMapping.ContainsKey(cleanSymbol))
{
zerodhaInstrumentsMapping[cleanSymbol] = new List<SymbolData>();
}
zerodhaInstrumentsMapping[cleanSymbol].Add(new SymbolData(tp.InstrumentToken,market));
}
if (securityType == SecurityType.Equity)
{
var cleanSymbol = tp.TradingSymbol.Trim().Replace(" ", "");
var symbol = GetLeanSymbol(cleanSymbol, securityType, market);
symbols.Add(symbol);
if (!zerodhaInstrumentsMapping.ContainsKey(cleanSymbol))
{
zerodhaInstrumentsMapping[cleanSymbol] = new List<SymbolData>();
}
zerodhaInstrumentsMapping[cleanSymbol].Add(new SymbolData(tp.InstrumentToken,market));
}
}
ZerodhaInstrumentsList = zerodhaInstrumentsMapping;
ZerodhaInstrumentsExchangeMapping = zerodhaTokenExchangeDict;
return symbols;
}
private decimal GetStrikePrice(CsvInstrument scrip)
{
var strikePrice = scrip.TradingSymbol.Trim().Replace(" ", "").Replace(scrip.Name, "");
var strikePriceTemp = strikePrice.Substring(5, strikePrice.Length - 5);
var strikePriceResult = strikePriceTemp.Substring(0, strikePriceTemp.Length - 2);
return Convert.ToDecimal(strikePriceResult, CultureInfo.InvariantCulture);
}
/// <summary>
/// Converts a Lean symbol instance to an Zerodha symbol
/// </summary>
/// <param name="symbol">A Lean symbol instance</param>
/// <returns>The Zerodha symbol</returns>
public string GetBrokerageSymbol(Symbol symbol)
{
if (symbol == null || string.IsNullOrWhiteSpace(symbol.Value))
{
throw new ArgumentException("Invalid symbol: " + (symbol == null ? "null" : symbol.ToString()));
}
if (symbol.ID.SecurityType != SecurityType.Equity && symbol.ID.SecurityType != SecurityType.Future && symbol.ID.SecurityType != SecurityType.Option)
{
throw new ArgumentException("Invalid security type: " + symbol.ID.SecurityType);
}
var brokerageSymbol = ConvertLeanSymbolToZerodhaSymbol(symbol.Value);
return brokerageSymbol;
}
/// <summary>
/// Converts an Zerodha symbol to a Lean symbol instance
/// </summary>
/// <param name="brokerageSymbol">The Zerodha 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 = OptionRight.Call)
{
if (string.IsNullOrWhiteSpace(brokerageSymbol))
{
throw new ArgumentException($"Invalid Zerodha symbol: {brokerageSymbol}");
}
if (securityType == SecurityType.Forex || securityType == SecurityType.Cfd || securityType == SecurityType.Commodity || securityType == SecurityType.Crypto)
{
throw new ArgumentException($"Invalid security type: {securityType}");
}
if (!Market.Encode(market).HasValue)
{
throw new ArgumentException($"Invalid market: {market}");
}
var cleanSymbol = brokerageSymbol.Replace(" ", "").Trim();
switch (securityType)
{
case SecurityType.Option:
OptionStyle optionStyle = OptionStyle.European;
return Symbol.CreateOption(cleanSymbol, market, optionStyle, optionRight, strike, expirationDate);
case SecurityType.Future:
return Symbol.CreateFuture(cleanSymbol, market, expirationDate);
default:
return Symbol.Create(cleanSymbol, securityType, market);
}
}
/// <summary>
/// Converts an Zerodha symbol to a Lean symbol instance
/// </summary>
/// <param name="brokerageSymbol">The Zerodha symbol</param>
/// <returns>A new Lean Symbol instance</returns>
public Symbol GetLeanSymbol(string brokerageSymbol)
{
if (string.IsNullOrWhiteSpace(brokerageSymbol))
{
throw new ArgumentException($"Invalid Zerodha symbol: {brokerageSymbol}");
}
var cleanSymbol = brokerageSymbol.Replace(" ", "").Trim();
if (IsKnownBrokerageSymbol(cleanSymbol))
{
throw new ArgumentException($"Symbol not present : {cleanSymbol}");
}
var symbol = KnownSymbols.FirstOrDefault(s => s.Value == cleanSymbol);
var exchange = GetZerodhaDefaultExchange(cleanSymbol);
return GetLeanSymbol(cleanSymbol, symbol.SecurityType, exchange);
}
/// <summary>
/// Fetches the trading segment inside India Market, E.g: NSE, BSE for the given Instrument Token
/// </summary>
/// <param name="Token">The Zerodha Instrument Token</param>
/// <returns>An exchange value for the given token</returns>
public string GetZerodhaExchangeFromToken(uint Token)
{
string exchange = string.Empty;
if (ZerodhaInstrumentsExchangeMapping.ContainsKey(Token))
{
ZerodhaInstrumentsExchangeMapping.TryGetValue(Token, out exchange);
}
return exchange;
}
/// <summary>
/// Fetches the first available Exchage value for the given symbol from list of possible exchanges
/// </summary>
/// <param name="brokerageSymbol">The Zerodha symbol</param>
/// <returns>A default exchange value for the given ticker</returns>
private string GetZerodhaDefaultExchange(string brokerageSymbol)
{
if (string.IsNullOrWhiteSpace(brokerageSymbol))
{
throw new ArgumentException($"Invalid Zerodha symbol: {brokerageSymbol}");
}
var cleanSymbol = brokerageSymbol.Replace(" ", "").Trim();
List<SymbolData> tempSymbolDataList;
if (ZerodhaInstrumentsList.TryGetValue(cleanSymbol, out tempSymbolDataList))
{
return tempSymbolDataList[0].Exchange;
}
return string.Empty;
}
/// <summary>
/// Converts Lean symbol to a List of Zerodha Instrument Tokens available from various exchange
/// </summary>
/// <param name="brokerageSymbol">The Zerodha symbol</param>
/// <returns>A list of Zerodha Instrument Tokens</returns>
public List<uint> GetZerodhaInstrumentTokenList(string brokerageSymbol)
{
if (string.IsNullOrWhiteSpace(brokerageSymbol))
{
throw new ArgumentException($"Invalid Zerodha symbol: {brokerageSymbol}");
}
var cleanSymbol = brokerageSymbol.Replace(" ", "").Trim();
List<uint> tokenList = new List<uint>();
List<SymbolData> tempSymbolDataList;
if (ZerodhaInstrumentsList.TryGetValue(cleanSymbol, out tempSymbolDataList))
{
foreach (var sd in tempSymbolDataList)
{
tokenList.Add(sd.InstrumentToken);
}
}
return tokenList;
}
/// <summary>
/// Checks if the symbol is supported by Zerodha
/// </summary>
/// <param name="brokerageSymbol">The Zerodha symbol</param>
/// <returns>True if Zerodha supports the symbol</returns>
private bool IsKnownBrokerageSymbol(string brokerageSymbol)
{
if (string.IsNullOrWhiteSpace(brokerageSymbol))
{
return false;
}
return KnownSymbolsList.Where(x => x.Value.Contains(brokerageSymbol)).IsNullOrEmpty();
}
/// <summary>
/// Converts an Zerodha symbol to a Lean symbol string
/// </summary>
public Symbol ConvertZerodhaSymbolToLeanSymbol(uint ZerodhaSymbol)
{
var _symbol = string.Empty;
foreach (var item in ZerodhaInstrumentsList)
{
foreach( var sd in item.Value)
{
if (sd.InstrumentToken == ZerodhaSymbol)
{
_symbol = item.Key;
break;
}
}
}
// return as it is due to Zerodha has similar Symbol format
return KnownSymbolsList.Where(s => s.Value == _symbol).FirstOrDefault();
}
/// <summary>
/// Converts a Lean symbol string to an Zerodha symbol
/// </summary>
private static string ConvertLeanSymbolToZerodhaSymbol(string leanSymbol)
{
if (string.IsNullOrWhiteSpace(leanSymbol))
{
throw new ArgumentException($"Invalid Lean symbol: {leanSymbol}");
}
// return as it is due to Zerodha has similar Symbol format
return leanSymbol.ToUpperInvariant();
}
}
}

View File

@@ -40,6 +40,7 @@ from QuantConnect.Python import *
from QuantConnect.Storage import *
from QuantConnect.Research import *
from QuantConnect.Algorithm import *
from QuantConnect.Statistics import *
from QuantConnect.Parameters import *
from QuantConnect.Benchmarks import *
from QuantConnect.Brokerages import *

View File

@@ -167,7 +167,7 @@ namespace QuantConnect.Brokerages
return 1m;
}
if (security.Type == SecurityType.Equity || security.Type == SecurityType.Future || security.Type == SecurityType.Option)
if (security.Type == SecurityType.Equity || security.Type == SecurityType.Future || security.Type == SecurityType.Option || security.Type == SecurityType.Index)
{
return _maxLeverage;
}

View File

@@ -1,4 +1,4 @@
/*
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
@@ -164,7 +164,7 @@ namespace QuantConnect.Brokerages
return 1m;
}
if (security.Type == SecurityType.Equity || security.Type == SecurityType.Future || security.Type == SecurityType.Option)
if (security.Type == SecurityType.Equity || security.Type == SecurityType.Future || security.Type == SecurityType.Option || security.Type == SecurityType.Index)
{
return _maxLeverage;
}

View File

@@ -127,15 +127,16 @@ namespace QuantConnect.Data
/// <param name="config">The subscription data configuration we are processing</param>
/// <remarks>One of the objectives of this method is to normalize the 'use price scale'
/// check and void code duplication and related issues</remarks>
/// <param name="liveMode">True, is this is a live mode data stream</param>
/// <returns>True if ticker prices should be scaled</returns>
public static bool PricesShouldBeScaled(this SubscriptionDataConfig config)
public static bool PricesShouldBeScaled(this SubscriptionDataConfig config, bool liveMode = false)
{
if (config.IsCustomData || config.Symbol.Value.Contains("UNIVERSE"))
{
return false;
}
if(config.SecurityType == SecurityType.Equity)
if(config.SecurityType == SecurityType.Equity && !liveMode)
{
return true;
}

View File

@@ -139,6 +139,9 @@ namespace QuantConnect.Orders.Fees
case Market.USA:
equityFee = new EquityFee(Currencies.USD, feePerShare: 0.005m, minimumFee: 1, maximumFeeRate: 0.005m);
break;
case Market.India:
equityFee = new EquityFee(Currencies.INR, feePerShare: 0.01m, minimumFee: 6, maximumFeeRate: 20);
break;
default:
throw new KeyNotFoundException($"InteractiveBrokersFeeModel(): unexpected equity Market {market}");
}

View File

@@ -27,6 +27,7 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using QuantConnect.Configuration;
using QuantConnect.Data;
using QuantConnect.Logging;
namespace QuantConnect.Util
@@ -241,8 +242,10 @@ namespace QuantConnect.Util
/// </summary>
/// <typeparam name="T">The type of the export</typeparam>
/// <param name="typeName">The name of the type to find. This can be an assembly qualified name, a full name, or just the type's name</param>
/// <param name="forceTypeNameOnExisting">When false, if any existing instance of type T is found, it will be returned even if type name doesn't match.
/// This is useful in cases where a single global instance is desired, like for <see cref="IDataAggregator"/></param>
/// <returns>The export instance</returns>
public T GetExportedValueByTypeName<T>(string typeName)
public T GetExportedValueByTypeName<T>(string typeName, bool forceTypeNameOnExisting = true)
where T : class
{
try
@@ -254,8 +257,8 @@ namespace QuantConnect.Util
var type = typeof(T);
if (_exportedValues.TryGetValue(type, out values))
{
// if we've alread loaded this part, then just return the same one
instance = values.OfType<T>().FirstOrDefault(x => x.GetType().MatchesTypeName(typeName));
// if we've already loaded this part, then just return the same one
instance = values.OfType<T>().FirstOrDefault(x => !forceTypeNameOnExisting || x.GetType().MatchesTypeName(typeName));
if (instance != null)
{
return instance;

View File

@@ -38,8 +38,8 @@ namespace QuantConnect.Util
/// The different <see cref="SecurityType"/> used for data paths
/// </summary>
/// <remarks>This includes 'alternative'</remarks>
public static IReadOnlyList<string> SecurityTypeAsDataPath => Enum.GetNames(typeof(SecurityType))
.Select(x => x.ToLowerInvariant()).Union(new[] { "alternative" }).ToList();
public static HashSet<string> SecurityTypeAsDataPath => Enum.GetNames(typeof(SecurityType))
.Select(x => x.ToLowerInvariant()).Union(new[] { "alternative" }).ToHashSet();
/// <summary>
/// Converts the specified base data instance into a lean data file csv line.
@@ -966,9 +966,11 @@ namespace QuantConnect.Util
/// </summary>
/// <param name="fileName">File name to be parsed</param>
/// <param name="securityType">The securityType as parsed from the fileName</param>
public static bool TryParseSecurityType(string fileName, out SecurityType securityType)
/// <param name="market">The market as parsed from the fileName</param>
public static bool TryParseSecurityType(string fileName, out SecurityType securityType, out string market)
{
securityType = SecurityType.Base;
market = string.Empty;
try
{
@@ -977,6 +979,13 @@ namespace QuantConnect.Util
// find the securityType and parse it
var typeString = info.Find(x => SecurityTypeAsDataPath.Contains(x.ToLowerInvariant()));
securityType = ParseDataSecurityType(typeString);
var existingMarkets = Market.SupportedMarkets();
var foundMarket = info.Find(x => existingMarkets.Contains(x.ToLowerInvariant()));
if (foundMarket != null)
{
market = foundMarket;
}
}
catch (Exception e)
{

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -36059,6 +36059,328 @@
],
"holidays": []
},
"Index-india-[*]": {
"dataTimeZone": "Asia/Kolkata",
"exchangeTimeZone": "Asia/Kolkata",
"sunday": [],
"monday": [
{
"start": "09:00:00",
"end": "09:15:00",
"state": "premarket"
},
{
"start": "09:15:00",
"end": "15:30:00",
"state": "market"
},
{
"start": "15:40:00",
"end": "16:00:00",
"state": "postmarket"
}
],
"tuesday": [
{
"start": "09:00:00",
"end": "09:15:00",
"state": "premarket"
},
{
"start": "09:15:00",
"end": "15:30:00",
"state": "market"
},
{
"start": "15:40:00",
"end": "16:00:00",
"state": "postmarket"
}
],
"wednesday": [
{
"start": "09:00:00",
"end": "09:15:00",
"state": "premarket"
},
{
"start": "09:15:00",
"end": "15:30:00",
"state": "market"
},
{
"start": "15:40:00",
"end": "16:00:00",
"state": "postmarket"
}
],
"thursday": [
{
"start": "09:00:00",
"end": "09:15:00",
"state": "premarket"
},
{
"start": "09:15:00",
"end": "15:30:00",
"state": "market"
},
{
"start": "15:40:00",
"end": "16:00:00",
"state": "postmarket"
}
],
"friday": [
{
"start": "09:00:00",
"end": "09:15:00",
"state": "premarket"
},
{
"start": "09:15:00",
"end": "15:30:00",
"state": "market"
},
{
"start": "15:40:00",
"end": "16:00:00",
"state": "postmarket"
}
],
"saturday": [],
"holidays": [
"1/26/2004",
"4/14/2004",
"1/26/2005",
"4/14/2005",
"8/15/2005",
"1/26/2006",
"4/14/2006",
"5/1/2006",
"8/15/2006",
"10/2/2006",
"12/25/2006",
"1/26/2007",
"5/1/2007",
"8/15/2007",
"10/2/2007",
"12/25/2007",
"4/14/2008",
"5/1/2008",
"8/15/2008",
"10/2/2008",
"12/25/2008",
"1/26/2009",
"4/14/2009",
"5/1/2009",
"10/2/2009",
"12/25/2009",
"1/26/2010",
"4/14/2010",
"1/26/2011",
"3/2/2011",
"4/12/2011",
"4/14/2011",
"4/22/2011",
"8/15/2011",
"8/31/2011",
"9/1/2011",
"10/6/2011",
"10/26/2011",
"10/27/2011",
"11/7/2011",
"11/10/2011",
"12/6/2011",
"1/26/2012",
"2/20/2012",
"3/8/2012",
"4/5/2012",
"4/6/2012",
"5/1/2012",
"8/15/2012",
"8/20/2012",
"9/19/2012",
"10/2/2012",
"10/24/2012",
"11/14/2012",
"11/28/2012",
"12/25/2012",
"3/27/2013",
"3/29/2013",
"4/19/2013",
"4/24/2013",
"5/1/2013",
"8/9/2013",
"8/15/2013",
"9/9/2013",
"10/2/2013",
"10/16/2013",
"11/4/2013",
"11/15/2013",
"12/25/2013",
"2/27/2014",
"3/17/2014",
"4/8/2014",
"4/14/2014",
"4/18/2014",
"4/24/2014",
"5/1/2014",
"7/29/2014",
"8/15/2014",
"8/29/2014",
"10/2/2014",
"10/3/2014",
"10/6/2014",
"10/15/2014",
"10/24/2014",
"11/4/2014",
"11/6/2014",
"12/25/2014",
"1/26/2015",
"2/17/2015",
"3/6/2015",
"4/2/2015",
"4/3/2015",
"4/14/2015",
"5/1/2015",
"9/17/2015",
"9/25/2015",
"10/2/2015",
"10/22/2015",
"11/12/2015",
"11/25/2015",
"12/25/2015",
"1/26/2016",
"3/7/2016",
"3/24/2016",
"3/25/2016",
"4/14/2016",
"4/15/2016",
"4/19/2016",
"7/6/2016",
"8/15/2016",
"9/5/2016",
"9/13/2016",
"10/11/2016",
"10/12/2016",
"10/31/2016",
"11/14/2016",
"1/26/2017",
"2/24/2017",
"3/13/2017",
"4/4/2017",
"4/14/2017",
"5/1/2017",
"6/26/2017",
"8/15/2017",
"8/25/2017",
"10/2/2017",
"10/20/2017",
"12/25/2017",
"1/26/2018",
"2/13/2018",
"3/2/2018",
"3/29/2018",
"3/30/2018",
"5/1/2018",
"8/15/2018",
"8/22/2018",
"9/13/2018",
"9/20/2018",
"10/2/2018",
"10/18/2018",
"11/8/2018",
"11/23/2018",
"12/25/2018",
"3/4/2019",
"3/21/2019",
"4/17/2019",
"4/19/2019",
"4/29/2019",
"5/1/2019",
"6/5/2019",
"8/12/2019",
"8/15/2019",
"9/2/2019",
"9/10/2019",
"10/2/2019",
"10/8/2019",
"10/21/2019",
"10/28/2019",
"11/12/2019",
"12/25/2019",
"2/21/2020",
"3/10/2020",
"4/2/2020",
"4/6/2020",
"4/10/2020",
"4/14/2020",
"5/1/2020",
"5/25/2020",
"10/2/2020",
"11/16/2020",
"11/30/2020",
"12/25/2020",
"1/26/2021",
"3/11/2021",
"3/29/2021",
"4/2/2021",
"4/14/2021",
"4/21/2021",
"5/13/2021",
"7/21/2021",
"8/19/2021",
"9/10/2021",
"10/15/2021",
"11/5/2021",
"11/19/2021",
"1/26/2022",
"4/14/2022",
"8/15/2022",
"1/26/2023",
"4/14/2023",
"5/1/2023",
"8/15/2023",
"10/2/2023",
"12/25/2023",
"1/26/2024",
"5/1/2024",
"8/15/2024",
"10/2/2024",
"12/25/2024",
"4/14/2025",
"5/1/2025",
"8/15/2025",
"10/2/2025",
"12/25/2025",
"1/26/2026",
"4/14/2026",
"5/1/2026",
"10/2/2026",
"12/25/2026",
"1/26/2027",
"4/14/2027",
"1/26/2028",
"4/14/2028",
"5/1/2028",
"8/15/2028",
"10/2/2028",
"12/25/2028",
"1/26/2029",
"5/1/2029",
"8/15/2029",
"10/2/2029",
"12/25/2029",
"5/1/2030",
"8/15/2030",
"10/2/2030",
"12/25/2030"
],
"earlyCloses": {}
},
"Equity-india-[*]": {
"dataTimeZone": "Asia/Kolkata",
"exchangeTimeZone": "Asia/Kolkata",

View File

@@ -41,7 +41,8 @@ namespace QuantConnect.Lean.Engine.DataFeeds
private readonly HashSet<SecurityType> _unsupportedSecurityType;
private readonly DataPricesList _dataPrices;
private readonly Api.Api _api;
private readonly bool _subscribedToEquityMapAndFactorFiles;
private readonly bool _subscribedToIndiaEquityMapAndFactorFiles;
private readonly bool _subscribedToUsaEquityMapAndFactorFiles;
private readonly bool _subscribedToFutureMapAndFactorFiles;
private volatile bool _invalidSecurityTypeLog;
@@ -71,13 +72,18 @@ namespace QuantConnect.Lean.Engine.DataFeeds
if (productItem.Id == 37)
{
// Determine if the user is subscribed to Equity map and factor files (Data product Id 37)
_subscribedToEquityMapAndFactorFiles = true;
_subscribedToUsaEquityMapAndFactorFiles = true;
}
else if (productItem.Id == 137)
{
// Determine if the user is subscribed to Future map and factor files (Data product Id 137)
_subscribedToFutureMapAndFactorFiles = true;
}
else if (productItem.Id == 172)
{
// Determine if the user is subscribed to India map and factor files (Data product Id 172)
_subscribedToIndiaEquityMapAndFactorFiles = true;
}
}
// Verify user has agreed to data provider agreements
@@ -161,7 +167,7 @@ namespace QuantConnect.Lean.Engine.DataFeeds
}
// Some security types can't be downloaded, lets attempt to extract that information
if (LeanData.TryParseSecurityType(filePath, out SecurityType securityType) && _unsupportedSecurityType.Contains(securityType))
if (LeanData.TryParseSecurityType(filePath, out SecurityType securityType, out var market) && _unsupportedSecurityType.Contains(securityType))
{
// we do support future auxiliary data (map and factor files)
if (securityType != SecurityType.Future || !IsAuxiliaryData(filePath))
@@ -192,12 +198,20 @@ namespace QuantConnect.Lean.Engine.DataFeeds
}
}
// Final check; If we want to download and the request requires equity data we need to be sure they are subscribed to map and factor files
else if (!_subscribedToEquityMapAndFactorFiles && (securityType == SecurityType.Equity || securityType == SecurityType.Option || IsAuxiliaryData(filePath)))
else if (!_subscribedToUsaEquityMapAndFactorFiles && market.Equals(Market.USA, StringComparison.InvariantCultureIgnoreCase)
&& (securityType == SecurityType.Equity || securityType == SecurityType.Option || IsAuxiliaryData(filePath)))
{
throw new ArgumentException("ApiDataProvider(): Must be subscribed to map and factor files to use the ApiDataProvider " +
"to download Equity data from QuantConnect. " +
"Please visit https://www.quantconnect.com/datasets/quantconnect-security-master for details.");
}
else if (!_subscribedToIndiaEquityMapAndFactorFiles && market.Equals(Market.India, StringComparison.InvariantCultureIgnoreCase)
&& (securityType == SecurityType.Equity || securityType == SecurityType.Option || IsAuxiliaryData(filePath)))
{
throw new ArgumentException("ApiDataProvider(): Must be subscribed to map and factor files to use the ApiDataProvider " +
"to download India data from QuantConnect. " +
"Please visit https://www.quantconnect.com/datasets/truedata-india-equity-security-master for details.");
}
}
return shouldDownload;

View File

@@ -33,8 +33,9 @@ namespace QuantConnect.Lean.Engine.DataFeeds.Enumerators
private readonly IEnumerator<BaseData> _rawDataEnumerator;
private readonly SubscriptionDataConfig _config;
private readonly IFactorFileProvider _factorFileProvider;
private DateTime _lastTradableDate;
private DateTime _nextTradableDate;
private IFactorProvider _factorFile;
private bool _liveMode;
/// <summary>
/// Explicit interface implementation for <see cref="Current"/>
@@ -57,13 +58,16 @@ namespace QuantConnect.Lean.Engine.DataFeeds.Enumerators
/// <param name="config">The <see cref="SubscriptionDataConfig"/> to enumerate for.
/// Will determine the <see cref="DataNormalizationMode"/> to use.</param>
/// <param name="factorFileProvider">The <see cref="IFactorFileProvider"/> instance to use</param>
/// <param name="liveMode">True, is this is a live mode data stream</param>
public PriceScaleFactorEnumerator(
IEnumerator<BaseData> rawDataEnumerator,
SubscriptionDataConfig config,
IFactorFileProvider factorFileProvider)
IFactorFileProvider factorFileProvider,
bool liveMode = false)
{
_lastTradableDate = DateTime.MinValue;
_config = config;
_liveMode = liveMode;
_nextTradableDate = DateTime.MinValue;
_rawDataEnumerator = rawDataEnumerator;
_factorFileProvider = factorFileProvider;
}
@@ -93,11 +97,18 @@ namespace QuantConnect.Lean.Engine.DataFeeds.Enumerators
&& _factorFileProvider != null
&& _config.DataNormalizationMode != DataNormalizationMode.Raw)
{
if (Current.Time.Date > _lastTradableDate)
if (Current.Time >= _nextTradableDate)
{
_factorFile = _factorFileProvider.Get(_config.Symbol);
_lastTradableDate = Current.Time.Date;
_config.PriceScaleFactor = _factorFile.GetPriceScale(_lastTradableDate, _config.DataNormalizationMode, _config.ContractDepthOffset, _config.DataMappingMode);
_config.PriceScaleFactor = _factorFile.GetPriceScale(Current.Time.Date, _config.DataNormalizationMode, _config.ContractDepthOffset, _config.DataMappingMode);
// update factor files every day
_nextTradableDate = Current.Time.Date.AddDays(1);
if (_liveMode)
{
// in live trading we add a offset to make sure new factor files are available
_nextTradableDate = _nextTradableDate.Add(Time.LiveAuxiliaryDataOffset);
}
}
Current = Current.Normalize(_config.PriceScaleFactor, _config.DataNormalizationMode, _config.SumOfDividends);

View File

@@ -237,6 +237,17 @@ namespace QuantConnect.Lean.Engine.DataFeeds
}
}
// scale prices before 'SubscriptionFilterEnumerator' since it updates securities realtime price
// and before fill forwarding so we don't happen to apply twice the factor
if (request.Configuration.PricesShouldBeScaled(liveMode:true))
{
enumerator = new PriceScaleFactorEnumerator(
enumerator,
request.Configuration,
_factorFileProvider,
liveMode:true);
}
if (request.Configuration.FillDataForward)
{
var fillForwardResolution = _subscriptions.UpdateAndGetFillForwardResolution(request.Configuration);

View File

@@ -76,6 +76,16 @@ namespace QuantConnect.Tests.Brokerages.Tradier
return false;
}
[Test]
public void GetQuotesDoesNotReturnNull()
{
var tradier = (TradierBrokerage) Brokerage;
var quotes = tradier.GetQuotes(new List<string> { "VXX190517P00016000" });
Assert.IsNotNull(quotes);
Assert.IsEmpty(quotes);
}
/// <summary>
/// Gets the current market price of the specified security
/// </summary>

View File

@@ -1,41 +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 NUnit.Framework;
using QuantConnect.Brokerages.Zerodha.Messages;
namespace QuantConnect.Tests.Brokerages.Zerodha
{
[TestFixture]
public class KiteTests
{
[Test]
public void HistoricalCandleData()
{
var timeStamp = new DateTimeOffset(2021, 06, 11, 9, 15, 0, new TimeSpan(5, 30, 0));
List<object> param = new List<object>(){ timeStamp, "1575", "1610.5", "1572", "1608.75", "2179" };
var expectedCandleData = new Historical(param);
Assert.AreEqual(new DateTime(2021, 06, 11, 3, 45, 0), expectedCandleData.TimeStamp);
Assert.AreEqual(1575, expectedCandleData.Open);
Assert.AreEqual(1610.5, expectedCandleData.High);
Assert.AreEqual(1572, expectedCandleData.Low);
Assert.AreEqual(1608.75, expectedCandleData.Close);
Assert.AreEqual(2179, expectedCandleData.Volume);
}
}
}

View File

@@ -1,100 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using NUnit.Framework;
using QuantConnect.Brokerages.Zerodha;
using QuantConnect.Configuration;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Logging;
using QuantConnect.Securities;
namespace QuantConnect.Tests.Brokerages.Zerodha
{
[TestFixture, Ignore("This test requires a configured and active Zerodha account")]
public class ZerodhaBrokerageHistoryProviderTests
{
private static TestCaseData[] TestParameters
{
get
{
return new[]
{
// valid parameters
new TestCaseData(Symbols.SBIN, Resolution.Tick, Time.OneMinute, false),
new TestCaseData(Symbols.SBIN, Resolution.Second, Time.OneMinute, false),
new TestCaseData(Symbols.SBIN, Resolution.Minute, Time.OneHour, false),
new TestCaseData(Symbols.SBIN, Resolution.Hour, Time.OneDay, false),
new TestCaseData(Symbols.SBIN, Resolution.Daily, TimeSpan.FromDays(15), false),
// invalid period, throws "System.ArgumentException : Invalid date range specified"
new TestCaseData(Symbols.SBIN, Resolution.Daily, TimeSpan.FromDays(-15), true),
// invalid security type, throws "System.ArgumentException : Invalid security type: Forex"
new TestCaseData(Symbols.EURUSD, Resolution.Daily, TimeSpan.FromDays(15), true)
};
}
}
[Test, TestCaseSource(nameof(TestParameters))]
public void GetsHistory(Symbol symbol, Resolution resolution, TimeSpan period, bool throwsException)
{
TestDelegate test = () =>
{
var accessToken = Config.Get("zerodha-access-token");
var apiKey = Config.Get("zerodha-api-key");
var tradingSegment = Config.Get("zerodha-trading-segment");
var productType = Config.Get("zerodha-product-type");
var brokerage = new ZerodhaBrokerage(tradingSegment, productType, apiKey, accessToken, null,null,null);
var now = DateTime.UtcNow;
var request = new HistoryRequest(now.Add(-period),
now,
typeof(QuoteBar),
symbol,
resolution,
SecurityExchangeHours.AlwaysOpen(TimeZones.Kolkata),
TimeZones.Kolkata,
Resolution.Minute,
false,
false,
DataNormalizationMode.Adjusted,
TickType.Quote)
{ };
var history = brokerage.GetHistory(request);
foreach (var slice in history)
{
Log.Trace("{0}: {1} - {2} / {3}", slice.Time, slice.Symbol, slice.Price, slice.IsFillForward);
}
Log.Trace("Base currency: " + brokerage.AccountBaseCurrency);
};
if (throwsException)
{
Assert.Throws<ArgumentException>(test);
}
else
{
Assert.DoesNotThrow(test);
}
}
}
}

View File

@@ -1,706 +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 Moq;
using NUnit.Framework;
using QuantConnect.Brokerages;
using QuantConnect.Brokerages.Zerodha;
using QuantConnect.Configuration;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
using QuantConnect.Lean.Engine.DataFeeds;
using QuantConnect.Logging;
using QuantConnect.Orders;
using QuantConnect.Securities;
using QuantConnect.Tests.Common.Securities;
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace QuantConnect.Tests.Brokerages.Zerodha
{
[TestFixture, Ignore("This test requires a configured and active Zerodha account")]
public class ZerodhaBrokerageTests
{
private static IOrderProperties orderProperties = new IndiaOrderProperties(exchange: Exchange.NSE);
private IBrokerage _brokerage;
private OrderProvider _orderProvider;
private SecurityProvider _securityProvider;
[SetUp]
public void Setup()
{
Log.Trace("");
Log.Trace("");
Log.Trace("--- SETUP ---");
Log.Trace("");
Log.Trace("");
// we want to regenerate these for each test
_brokerage = null;
_orderProvider = null;
_securityProvider = null;
Thread.Sleep(1000);
CancelOpenOrders();
LiquidateZerodhaHoldings();
Thread.Sleep(1000);
}
[TearDown]
public void Teardown()
{
try
{
Log.Trace("");
Log.Trace("");
Log.Trace("--- TEARDOWN ---");
Log.Trace("");
Log.Trace("");
Thread.Sleep(1000);
CancelOpenOrders();
LiquidateZerodhaHoldings();
Thread.Sleep(1000);
}
finally
{
if (_brokerage != null)
{
DisposeBrokerage(_brokerage);
}
}
}
/// <summary>
/// Disposes of the brokerage and any external resources started in order to create it
/// </summary>
/// <param name="brokerage">The brokerage instance to be disposed of</param>
protected virtual void DisposeBrokerage(IBrokerage brokerage)
{
brokerage.Disconnect();
}
public IBrokerage Brokerage
{
get
{
if (_brokerage == null)
{
_brokerage = InitializeBrokerage();
}
return _brokerage;
}
}
private IBrokerage InitializeBrokerage()
{
Log.Trace("");
Log.Trace("- INITIALIZING BROKERAGE -");
Log.Trace("");
var brokerage = CreateBrokerage(OrderProvider, SecurityProvider);
brokerage.Connect();
if (!brokerage.IsConnected)
{
Assert.Fail("Failed to connect to brokerage");
}
Log.Trace("");
Log.Trace("GET OPEN ORDERS");
Log.Trace("");
foreach (var openOrder in brokerage.GetOpenOrders())
{
OrderProvider.Add(openOrder);
}
Log.Trace("");
Log.Trace("GET ACCOUNT HOLDINGS");
Log.Trace("");
foreach (var accountHolding in brokerage.GetAccountHoldings())
{
// these securities don't need to be real, just used for the ISecurityProvider impl, required
// by brokerages to track holdings
SecurityProvider[accountHolding.Symbol] = CreateSecurity(accountHolding.Symbol);
}
brokerage.OrderStatusChanged += (sender, args) =>
{
Log.Trace("");
Log.Trace("ORDER STATUS CHANGED: " + args);
Log.Trace("");
// we need to keep this maintained properly
if (args.Status == OrderStatus.Filled || args.Status == OrderStatus.PartiallyFilled)
{
Log.Trace("FILL EVENT: " + args.FillQuantity + " units of " + args.Symbol.ToString());
Security security;
if (_securityProvider.TryGetValue(args.Symbol, out security))
{
var holding = _securityProvider[args.Symbol].Holdings;
holding.SetHoldings(args.FillPrice, holding.Quantity + args.FillQuantity);
}
else
{
_securityProvider[args.Symbol] = CreateSecurity(args.Symbol);
_securityProvider[args.Symbol].Holdings.SetHoldings(args.FillPrice, args.FillQuantity);
}
Log.Trace("--HOLDINGS: " + _securityProvider[args.Symbol]);
// update order mapping
var order = _orderProvider.GetOrderById(args.OrderId);
order.Status = args.Status;
}
};
return brokerage;
}
internal static Security CreateSecurity(Symbol symbol)
{
return new Security(
SecurityExchangeHours.AlwaysOpen(TimeZones.NewYork),
new SubscriptionDataConfig(
typeof(TradeBar),
symbol,
Resolution.Minute,
TimeZones.NewYork,
TimeZones.NewYork,
false,
false,
false
),
new Cash(Currencies.USD, 0, 1m),
SymbolProperties.GetDefault(Currencies.USD),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null,
new SecurityCache()
);
}
public OrderProvider OrderProvider
{
get { return _orderProvider ?? (_orderProvider = new OrderProvider()); }
}
public SecurityProvider SecurityProvider
{
get { return _securityProvider ?? (_securityProvider = new SecurityProvider()); }
}
/// <summary>
/// This is used to ensure each test starts with a clean, known state.
/// </summary>
protected void LiquidateZerodhaHoldings()
{
Log.Trace("");
Log.Trace("LIQUIDATE HOLDINGS");
Log.Trace("");
var holdings = Brokerage.GetAccountHoldings();
foreach (var holding in holdings)
{
if (holding.Quantity == 0) continue;
Log.Trace("Liquidating: " + holding);
var order = new MarketOrder(holding.Symbol, -holding.Quantity, DateTime.UtcNow,properties:orderProperties);
_orderProvider.Add(order);
PlaceZerodhaOrderWaitForStatus(order, OrderStatus.Filled);
}
}
/// <summary>
/// Provides the data required to test each order type in various cases
/// </summary>
private static TestCaseData[] OrderParameters()
{
return new[]
{
new TestCaseData(new MarketOrderTestParameters(Symbols.IDEA,orderProperties)).SetName("MarketOrder"),
new TestCaseData(new LimitOrderTestParameters(Symbols.IDEA, 9.00m, 9.30m,orderProperties)).SetName("LimitOrder"),
new TestCaseData(new StopMarketOrderTestParameters(Symbols.IDEA, 10.50m, 9.50m,orderProperties)).SetName("StopMarketOrder"),
//new TestCaseData(new StopLimitOrderTestParameters(Symbols.IDEA, 9.00m, 10.50m,orderProperties)).SetName("StopLimitOrder")
};
}
/// <summary>
/// Creates the brokerage under test
/// </summary>
/// <returns>A connected brokerage instance</returns>
protected IBrokerage CreateBrokerage(IOrderProvider orderProvider, ISecurityProvider securityProvider)
{
var securities = new SecurityManager(new TimeKeeper(DateTime.UtcNow, TimeZones.Kolkata))
{
{ Symbol, CreateSecurity(Symbol) }
};
var transactions = new SecurityTransactionManager(null, securities);
transactions.SetOrderProcessor(new FakeOrderProcessor());
var algorithm = new Mock<IAlgorithm>();
algorithm.Setup(a => a.Transactions).Returns(transactions);
algorithm.Setup(a => a.BrokerageModel).Returns(new ZerodhaBrokerageModel());
algorithm.Setup(a => a.Portfolio).Returns(new SecurityPortfolioManager(securities, transactions));
var accessToken = Config.Get("zerodha-access-token");
var apiKey = Config.Get("zerodha-api-key");
var tradingSegment = Config.Get("zerodha-trading-segment");
var productType = Config.Get("zerodha-product-type");
var zerodha = new ZerodhaBrokerage(tradingSegment, productType, apiKey, accessToken, algorithm.Object, algorithm.Object.Portfolio, new AggregationManager());
return zerodha;
}
/// <summary>
/// Gets the symbol to be traded, must be shortable
/// </summary>
protected Symbol Symbol => Symbols.IDEA;
/// <summary>
/// Gets the security type associated with the <see cref="BrokerageTests.Symbol"/>
/// </summary>
protected SecurityType SecurityType => SecurityType.Equity;
/// <summary>
/// Returns wether or not the brokers order methods implementation are async
/// </summary>
protected bool IsAsync()
{
return false;
}
/// <summary>
/// Gets the default order quantity
/// </summary>
protected virtual decimal GetDefaultQuantity()
{
return 1;
}
/// <summary>
/// Gets the current market price of the specified security
/// </summary>
protected decimal GetAskPrice(Symbol symbol)
{
var zerodha = (ZerodhaBrokerage)Brokerage;
var quotes = zerodha.GetQuote(symbol);
return quotes.LastPrice;
}
[Test]
public void ShortIdea()
{
PlaceZerodhaOrderWaitForStatus(new MarketOrder(Symbols.IDEA, -1, DateTime.Now, properties: orderProperties), OrderStatus.Submitted, allowFailedSubmission: true);
// wait for output to be generated
Thread.Sleep(20 * 1000);
}
[Test, TestCaseSource(nameof(OrderParameters))]
public void CancelOrders(OrderTestParameters parameters)
{
const int secondsTimeout = 20;
Log.Trace("");
Log.Trace("CANCEL ORDERS");
Log.Trace("");
var order = PlaceZerodhaOrderWaitForStatus(parameters.CreateLongOrder(GetDefaultQuantity()), parameters.ExpectedStatus);
var canceledOrderStatusEvent = new ManualResetEvent(false);
EventHandler<OrderEvent> orderStatusCallback = (sender, fill) =>
{
if (fill.Status == OrderStatus.Canceled)
{
canceledOrderStatusEvent.Set();
}
};
Brokerage.OrderStatusChanged += orderStatusCallback;
var cancelResult = false;
try
{
cancelResult = Brokerage.CancelOrder(order);
}
catch (Exception exception)
{
Log.Error(exception);
}
Assert.AreEqual(IsCancelAsync() || parameters.ExpectedCancellationResult, cancelResult);
if (parameters.ExpectedCancellationResult)
{
// We expect the OrderStatus.Canceled event
canceledOrderStatusEvent.WaitOneAssertFail(1000 * secondsTimeout, "Order timedout to cancel");
}
var openOrders = Brokerage.GetOpenOrders();
var cancelledOrder = openOrders.FirstOrDefault(x => x.Id == order.Id);
Assert.IsNull(cancelledOrder);
//canceledOrderStatusEvent.Reset();
//var cancelResultSecondTime = false;
//try
//{
// cancelResultSecondTime = Brokerage.CancelOrder(order);
//}
//catch (Exception exception)
//{
// Log.Error(exception);
//}
//Assert.AreEqual(IsCancelAsync(), cancelResultSecondTime);
//// We do NOT expect the OrderStatus.Canceled event
//Assert.IsFalse(canceledOrderStatusEvent.WaitOne(new TimeSpan(0, 0, 10)));
//Brokerage.OrderStatusChanged -= orderStatusCallback;
}
[Test]
public void ValidateStopLimitOrders()
{
var zerodha = (ZerodhaBrokerage)Brokerage;
var symbol = Symbol;
var lastPrice = GetAskPrice(symbol.Value);
// Buy StopLimit order below market TODO: This might not work because of the Zerodha structure. Verify this.
//var stopPrice = lastPrice - 0.10m;
//var limitPrice = stopPrice + 0.10m;
//var order = new StopLimitOrder(symbol, 1, stopPrice, limitPrice, DateTime.UtcNow, properties: orderProperties);
//Assert.IsTrue(zerodha.PlaceOrder(order));
// Buy StopLimit order above market
var stopPrice = lastPrice + 0.20m;
var limitPrice = stopPrice + 0.25m;
var order = new StopLimitOrder(symbol, 1, stopPrice, limitPrice, DateTime.UtcNow, properties: orderProperties);
Assert.IsTrue(zerodha.PlaceOrder(order));
// In case there is no position, the following sell orders would not be placed
// So build a position for them.
var marketOrder = new MarketOrder(symbol, 2, DateTime.UtcNow, properties: orderProperties);
Assert.IsTrue(zerodha.PlaceOrder(marketOrder));
Thread.Sleep(20000);
// Sell StopLimit order below market
stopPrice = lastPrice - 0.25m;
limitPrice = stopPrice - 0.5m;
order = new StopLimitOrder(symbol, -1, stopPrice, limitPrice, DateTime.UtcNow, properties: orderProperties);
Assert.IsTrue(zerodha.PlaceOrder(order));
// Sell StopLimit order above market. TODO: This might not work because of the Zerodha structure. Verify this
//stopPrice = lastPrice + 0.5m;
//limitPrice = stopPrice - 0.25m ;
//order = new StopLimitOrder(symbol, -1, stopPrice, limitPrice, DateTime.UtcNow, properties: orderProperties);
//Assert.IsTrue(zerodha.PlaceOrder(order));
}
[Test, TestCaseSource(nameof(OrderParameters))]
public void LongFromZero(OrderTestParameters parameters)
{
Log.Trace("");
Log.Trace("LONG FROM ZERO");
Log.Trace("");
PlaceZerodhaOrderWaitForStatus(parameters.CreateLongOrder(GetDefaultQuantity()), parameters.ExpectedStatus);
}
[Test, TestCaseSource(nameof(OrderParameters))]
public void CloseFromLong(OrderTestParameters parameters)
{
Log.Trace("");
Log.Trace("CLOSE FROM LONG");
Log.Trace("");
// first go long
PlaceZerodhaOrderWaitForStatus(parameters.CreateLongMarketOrder(GetDefaultQuantity()), OrderStatus.Filled);
// now close it
PlaceZerodhaOrderWaitForStatus(parameters.CreateShortOrder(GetDefaultQuantity()), parameters.ExpectedStatus);
}
[Test, TestCaseSource(nameof(OrderParameters))]
public void ShortFromZero(OrderTestParameters parameters)
{
Log.Trace("");
Log.Trace("SHORT FROM ZERO");
Log.Trace("");
PlaceZerodhaOrderWaitForStatus(parameters.CreateShortOrder(GetDefaultQuantity()), parameters.ExpectedStatus);
}
[Test, TestCaseSource(nameof(OrderParameters))]
public virtual void CloseFromShort(OrderTestParameters parameters)
{
Log.Trace("");
Log.Trace("CLOSE FROM SHORT");
Log.Trace("");
// first go short
PlaceZerodhaOrderWaitForStatus(parameters.CreateShortMarketOrder(GetDefaultQuantity()), OrderStatus.Filled);
// now close it
PlaceZerodhaOrderWaitForStatus(parameters.CreateLongOrder(GetDefaultQuantity()), parameters.ExpectedStatus);
}
[Test, TestCaseSource(nameof(OrderParameters))]
public virtual void ShortFromLong(OrderTestParameters parameters)
{
Log.Trace("");
Log.Trace("SHORT FROM LONG");
Log.Trace("");
// first go long
PlaceZerodhaOrderWaitForStatus(parameters.CreateLongMarketOrder(GetDefaultQuantity()));
// now go net short
var order = PlaceZerodhaOrderWaitForStatus(parameters.CreateShortOrder(2 * GetDefaultQuantity()), parameters.ExpectedStatus);
if (parameters.ModifyUntilFilled)
{
ModifyOrderUntilFilled(order, parameters);
}
}
[Test, Ignore("This test requires reading the output and selection of a low volume security for the Brokerage")]
public void PartialFills()
{
var manualResetEvent = new ManualResetEvent(false);
var qty = 1000000m;
var remaining = qty;
var sync = new object();
Brokerage.OrderStatusChanged += (sender, orderEvent) =>
{
lock (sync)
{
remaining -= orderEvent.FillQuantity;
Console.WriteLine("Remaining: " + remaining + " FillQuantity: " + orderEvent.FillQuantity);
if (orderEvent.Status == OrderStatus.Filled)
{
manualResetEvent.Set();
}
}
};
// pick a security with low, but some, volume
var symbol = Symbols.EURUSD;
var order = new MarketOrder(symbol, qty, DateTime.UtcNow) { Id = 1 };
OrderProvider.Add(order);
Brokerage.PlaceOrder(order);
// pause for a while to wait for fills to come in
manualResetEvent.WaitOne(2500);
manualResetEvent.WaitOne(2500);
manualResetEvent.WaitOne(2500);
Console.WriteLine("Remaining: " + remaining);
Assert.AreEqual(0, remaining);
}
[Test, TestCaseSource(nameof(OrderParameters))]
public virtual void LongFromShort(OrderTestParameters parameters)
{
Log.Trace("");
Log.Trace("LONG FROM SHORT");
Log.Trace("");
// first fo short
PlaceZerodhaOrderWaitForStatus(parameters.CreateShortMarketOrder(-GetDefaultQuantity()), OrderStatus.Filled);
// now go long
var order = PlaceZerodhaOrderWaitForStatus(parameters.CreateLongOrder(2 * GetDefaultQuantity()), parameters.ExpectedStatus);
if (parameters.ModifyUntilFilled)
{
ModifyOrderUntilFilled(order, parameters);
}
}
[Test]
public void GetCashBalanceContainsSomething()
{
Log.Trace("");
Log.Trace("GET CASH BALANCE");
Log.Trace("");
var balance = Brokerage.GetCashBalance();
Assert.IsTrue(balance.Any());
}
[Test]
public void GetAccountHoldings()
{
Log.Trace("");
Log.Trace("GET ACCOUNT HOLDINGS");
Log.Trace("");
var before = Brokerage.GetAccountHoldings();
PlaceZerodhaOrderWaitForStatus(new MarketOrder(Symbol, GetDefaultQuantity(), DateTime.UtcNow,properties:orderProperties));
Thread.Sleep(3000);
var after = Brokerage.GetAccountHoldings();
var beforeHoldings = before.FirstOrDefault(x => x.Symbol == Symbol);
var afterHoldings = after.FirstOrDefault(x => x.Symbol == Symbol);
var beforeQuantity = beforeHoldings == null ? 0 : beforeHoldings.Quantity;
var afterQuantity = afterHoldings == null ? 0 : afterHoldings.Quantity;
Assert.AreEqual(GetDefaultQuantity(), afterQuantity - beforeQuantity);
}
[Test]
public void IsConnected()
{
Assert.IsTrue(Brokerage.IsConnected);
}
/// <summary>
/// Places the specified order with the brokerage and wait until we get the <paramref name="expectedStatus"/> back via an OrderStatusChanged event.
/// This function handles adding the order to the <see cref="IOrderProvider"/> instance as well as incrementing the order ID.
/// </summary>
/// <param name="order">The order to be submitted</param>
/// <param name="expectedStatus">The status to wait for</param>
/// <param name="secondsTimeout">Maximum amount of time to wait for <paramref name="expectedStatus"/></param>
/// <param name="allowFailedSubmission">Allow failed order submission</param>
/// <returns>The same order that was submitted.</returns>
protected Order PlaceZerodhaOrderWaitForStatus(Order order, OrderStatus expectedStatus = OrderStatus.Filled,
double secondsTimeout = 10.0, bool allowFailedSubmission = false)
{
var requiredStatusEvent = new ManualResetEvent(false);
var desiredStatusEvent = new ManualResetEvent(false);
EventHandler<OrderEvent> brokerageOnOrderStatusChanged = (sender, args) =>
{
// no matter what, every order should fire at least one of these
if (args.Status == OrderStatus.Submitted || args.Status == OrderStatus.Invalid)
{
Log.Trace("");
Log.Trace("SUBMITTED: " + args);
Log.Trace("");
requiredStatusEvent.Set();
}
// make sure we fire the status we're expecting
if (args.Status == expectedStatus)
{
Log.Trace("");
Log.Trace("EXPECTED: " + args);
Log.Trace("");
desiredStatusEvent.Set();
}
};
Brokerage.OrderStatusChanged += brokerageOnOrderStatusChanged;
OrderProvider.Add(order);
if (!Brokerage.PlaceOrder(order) && !allowFailedSubmission)
{
Assert.Fail("Brokerage failed to place the order: " + order);
}
requiredStatusEvent.WaitOneAssertFail((int)(1000 * secondsTimeout), "Expected every order to fire a submitted or invalid status event");
desiredStatusEvent.WaitOneAssertFail((int)(1000 * secondsTimeout), "OrderStatus " + expectedStatus + " was not encountered within the timeout. Order Id:" + order.Id);
Brokerage.OrderStatusChanged -= brokerageOnOrderStatusChanged;
return order;
}
protected void CancelOpenOrders()
{
Log.Trace("");
Log.Trace("CANCEL OPEN ORDERS");
Log.Trace("");
var openOrders = Brokerage.GetOpenOrders();
foreach (var openOrder in openOrders)
{
Log.Trace("Canceling: " + openOrder);
Brokerage.CancelOrder(openOrder);
}
}
/// <summary>
/// Updates the specified order in the brokerage until it fills or reaches a timeout
/// </summary>
/// <param name="order">The order to be modified</param>
/// <param name="parameters">The order test parameters that define how to modify the order</param>
/// <param name="secondsTimeout">Maximum amount of time to wait until the order fills</param>
protected void ModifyOrderUntilFilled(Order order, OrderTestParameters parameters, double secondsTimeout = 90)
{
if (order.Status == OrderStatus.Filled)
{
return;
}
var filledResetEvent = new ManualResetEvent(false);
EventHandler<OrderEvent> brokerageOnOrderStatusChanged = (sender, args) =>
{
if (args.Status == OrderStatus.Filled)
{
filledResetEvent.Set();
}
if (args.Status == OrderStatus.Canceled || args.Status == OrderStatus.Invalid)
{
Log.Trace("ModifyOrderUntilFilled(): " + order);
Assert.Fail("Unexpected order status: " + args.Status);
}
};
Brokerage.OrderStatusChanged += brokerageOnOrderStatusChanged;
Log.Trace("");
Log.Trace("MODIFY UNTIL FILLED: " + order);
Log.Trace("");
var stopwatch = Stopwatch.StartNew();
while (!filledResetEvent.WaitOne(3000) && stopwatch.Elapsed.TotalSeconds < secondsTimeout)
{
filledResetEvent.Reset();
if (order.Status == OrderStatus.PartiallyFilled) continue;
var marketPrice = GetAskPrice(order.Symbol);
Log.Trace("BrokerageTests.ModifyOrderUntilFilled(): Ask: " + marketPrice);
var updateOrder = parameters.ModifyOrderToFill(Brokerage, order, marketPrice);
if (updateOrder)
{
if (order.Status == OrderStatus.Filled) break;
Log.Trace("BrokerageTests.ModifyOrderUntilFilled(): " + order);
if (!Brokerage.UpdateOrder(order))
{
Assert.Fail("Brokerage failed to update the order");
}
}
}
Brokerage.OrderStatusChanged -= brokerageOnOrderStatusChanged;
}
/// <summary>
/// Returns whether or not the brokers order cancel method implementation is async
/// </summary>
protected bool IsCancelAsync()
{
return false;
}
}
}

View File

@@ -23,6 +23,19 @@ namespace QuantConnect.Tests.Common.Util
[TestFixture]
public class ComposerTests
{
[Test]
public void GetExportedValueByTypeName()
{
var instance = Composer.Instance.GetExportedValueByTypeName<IExport2>("Export6");
Assert.AreEqual(typeof(Export6), instance.GetType());
var instance2 = Composer.Instance.GetExportedValueByTypeName<IExport2>("Export7");
Assert.AreEqual(typeof(Export7), instance2.GetType());
var instance3 = Composer.Instance.GetExportedValueByTypeName<IExport2>("Export8", forceTypeNameOnExisting: false);
Assert.AreNotEqual(typeof(Export8), instance3.GetType());
}
[Test]
public void ComposesTypes()
{
@@ -103,5 +116,24 @@ namespace QuantConnect.Tests.Common.Util
public int Id { get { return 5; } }
}
[InheritedExport(typeof(IExport2))]
interface IExport2
{
int Id { get; }
}
class Export6 : IExport2
{
public int Id { get { return 6; } }
}
class Export7 : IExport2
{
public int Id { get { return 7; } }
}
class Export8 : IExport2
{
public int Id { get { return 8; } }
}
}
}

View File

@@ -194,22 +194,23 @@ namespace QuantConnect.Tests.Common.Util
Assert.AreEqual(LeanData.GetCommonTickTypeForCommonDataTypes(typeof(Tick), SecurityType.Forex), TickType.Quote);
}
[TestCase("forex/fxcm/eurusd/20160101_quote.zip", true, SecurityType.Forex)]
[TestCase("Data/f/fxcm/eurusd/20160101_quote.zip", false, SecurityType.Base)]
[TestCase("ooooooooooooooooooooooooooooooooooooooooooooooooooooooo", false, SecurityType.Base)]
[TestCase("", false, SecurityType.Base)]
[TestCase(null, false, SecurityType.Base)]
[TestCase("forex/fxcm/eurusd/20160101_quote.zip", true, SecurityType.Forex, Market.FXCM)]
[TestCase("Data/f/fxcm/eurusd/20160101_quote.zip", false, SecurityType.Base, "")]
[TestCase("ooooooooooooooooooooooooooooooooooooooooooooooooooooooo", false, SecurityType.Base, "")]
[TestCase("", false, SecurityType.Base, "")]
[TestCase(null, false, SecurityType.Base, "")]
[TestCase("Data/option/u sa/minute/aapl/20140606_trade_american.zip", true, SecurityType.Option)]
[TestCase("../Data/equity/usa/daily/aapl.zip", true, SecurityType.Equity)]
[TestCase("Data/cfd/oanda/minute/bcousd/20160101_trade.zip", true, SecurityType.Cfd)]
[TestCase("Data\\alternative\\estimize\\consensus\\aapl.csv", true, SecurityType.Base)]
[TestCase("../../../Data/option/usa/minute/spy/20200922_quote_american.zip", true, SecurityType.Option)]
[TestCase("../../../Data/futureoption/comex/minute/og/20200428/20200105_quote_american.zip", true, SecurityType.FutureOption)]
public void TryParseSecurityType(string path, bool result, SecurityType expectedSecurityType)
[TestCase("Data/option/u sa/minute/aapl/20140606_trade_american.zip", true, SecurityType.Option, "")]
[TestCase("../Data/equity/usa/daily/aapl.zip", true, SecurityType.Equity, "usa")]
[TestCase("Data/cfd/oanda/minute/bcousd/20160101_trade.zip", true, SecurityType.Cfd, "oanda")]
[TestCase("Data\\alternative\\estimize\\consensus\\aapl.csv", true, SecurityType.Base, "")]
[TestCase("../../../Data/option/usa/minute/spy/20200922_quote_american.zip", true, SecurityType.Option, "usa")]
[TestCase("../../../Data/futureoption/comex/minute/og/20200428/20200105_quote_american.zip", true, SecurityType.FutureOption, "comex")]
public void TryParseSecurityType(string path, bool result, SecurityType expectedSecurityType, string market)
{
Assert.AreEqual(result, LeanData.TryParseSecurityType(path, out var securityType));
Assert.AreEqual(result, LeanData.TryParseSecurityType(path, out var securityType, out var parsedMarket));
Assert.AreEqual(expectedSecurityType, securityType);
Assert.AreEqual(market, parsedMarket);
}
[Test]

View File

@@ -31,7 +31,6 @@ namespace QuantConnect.Tests.Engine.DataFeeds
[TestFixture]
public class DataQueueHandlerManagerTests
{
[TestCase("ZerodhaBrokerage")]
[TestCase("TradierBrokerage")]
[TestCase("QuantConnect.Brokerages.InteractiveBrokers.InteractiveBrokersBrokerage")]
[TestCase("OandaBrokerage")]
@@ -51,7 +50,7 @@ namespace QuantConnect.Tests.Engine.DataFeeds
var dataHandlers = Newtonsoft.Json.JsonConvert.SerializeObject(new[] { "FakeDataQueue" });
var jobWithArrayIDQH = new LiveNodePacket
{
Brokerage = "ZerodhaBrokerage",
Brokerage = "OandaBrokerage",
DataQueueHandler = dataHandlers
};
var compositeDataQueueHandler = new DataQueueHandlerManager();
@@ -74,7 +73,7 @@ namespace QuantConnect.Tests.Engine.DataFeeds
var dataHandlers = Newtonsoft.Json.JsonConvert.SerializeObject(new[] { "FakeDataQueue" });
var job = new LiveNodePacket
{
Brokerage = "ZerodhaBrokerage",
Brokerage = "OandaBrokerage",
DataQueueHandler = dataHandlers
};
var compositeDataQueueHandler = new DataQueueHandlerManager();
@@ -107,7 +106,7 @@ namespace QuantConnect.Tests.Engine.DataFeeds
public void DoubleSubscribe()
{
var compositeDataQueueHandler = new DataQueueHandlerManager();
compositeDataQueueHandler.SetJob(new LiveNodePacket { Brokerage = "ZerodhaBrokerage", DataQueueHandler = "[ \"TestDataHandler\" ]" });
compositeDataQueueHandler.SetJob(new LiveNodePacket { Brokerage = "OandaBrokerage", DataQueueHandler = "[ \"TestDataHandler\" ]" });
var dataConfig = GetConfig();
var enumerator = compositeDataQueueHandler.Subscribe(dataConfig, (_, _) => {});
@@ -121,7 +120,7 @@ namespace QuantConnect.Tests.Engine.DataFeeds
{
TestDataHandler.UnsubscribeCounter = 0;
var compositeDataQueueHandler = new DataQueueHandlerManager();
compositeDataQueueHandler.SetJob(new LiveNodePacket { Brokerage = "ZerodhaBrokerage", DataQueueHandler = "[ \"TestDataHandler\" ]" });
compositeDataQueueHandler.SetJob(new LiveNodePacket { Brokerage = "OandaBrokerage", DataQueueHandler = "[ \"TestDataHandler\" ]" });
var dataConfig = GetConfig();
var enumerator = compositeDataQueueHandler.Subscribe(dataConfig, (_, _) => {});
@@ -142,7 +141,7 @@ namespace QuantConnect.Tests.Engine.DataFeeds
TestDataHandler.UnsubscribeCounter = 0;
TestDataHandler.SubscribeCounter = 0;
var compositeDataQueueHandler = new DataQueueHandlerManager();
compositeDataQueueHandler.SetJob(new LiveNodePacket { Brokerage = "ZerodhaBrokerage", DataQueueHandler = "[ \"TestDataHandler\" ]" });
compositeDataQueueHandler.SetJob(new LiveNodePacket { Brokerage = "OandaBrokerage", DataQueueHandler = "[ \"TestDataHandler\" ]" });
var canonicalSymbol = Symbols.ES_Future_Chain.UpdateMappedSymbol(Symbols.Future_ESZ18_Dec2018.ID.ToString());
var canonicalConfig = GetConfig(canonicalSymbol);

View File

@@ -644,6 +644,74 @@ namespace QuantConnect.Tests.Engine.DataFeeds
Log.Trace("Count: " + count + " ReaderCount: " + RestApiBaseData.ReaderCount);
}
[TestCase(DataNormalizationMode.Raw)]
[TestCase(DataNormalizationMode.BackwardsRatio)]
[TestCase(DataNormalizationMode.BackwardsPanamaCanal)]
[TestCase(DataNormalizationMode.ForwardPanamaCanal)]
public void LivePriceScaling(DataNormalizationMode dataNormalizationMode)
{
var feed = RunDataFeed();
_algorithm.SetFinishedWarmingUp();
var security = _algorithm.AddFuture("ES",
dataNormalizationMode: dataNormalizationMode);
var symbol = security.Symbol;
var receivedSecurityChanges = false;
var receivedData = false;
var assertPrice = new Action<decimal>((decimal price) =>
{
if (dataNormalizationMode == DataNormalizationMode.ForwardPanamaCanal && price < 150)
{
throw new Exception($"unexpected price {price} for {symbol} @{security.LocalTime}");
}
else if (dataNormalizationMode == DataNormalizationMode.Raw && price == 2)
{
throw new Exception($"unexpected price {price} for {symbol} @{security.LocalTime}");
}
else if (dataNormalizationMode == DataNormalizationMode.BackwardsPanamaCanal && price < -150)
{
throw new Exception($"unexpected price {price} for {symbol} @{security.LocalTime}");
}
else if (dataNormalizationMode == DataNormalizationMode.BackwardsRatio && Math.Abs(price - 1.48m) > price * 0.1m)
{
throw new Exception($"unexpected price {price} for {symbol} @{security.LocalTime}");
}
});
var lastPrice = 0m;
ConsumeBridge(feed, TimeSpan.FromSeconds(10), ts =>
{
foreach (var addedSecurity in ts.SecurityChanges.AddedSecurities)
{
if (addedSecurity.Symbol == symbol)
{
receivedSecurityChanges = true;
}
}
if (ts.Slice.Bars.ContainsKey(symbol))
{
receivedData = true;
assertPrice(ts.Slice.Bars[symbol].Price);
}
if (lastPrice != security.Price && security.HasData)
{
lastPrice = security.Price;
// assert realtime prices too
assertPrice(lastPrice);
}
},
alwaysInvoke: true,
secondsTimeStep: 60 * 60 * 8,
endDate: _startDate.AddDays(7));
Assert.IsTrue(receivedSecurityChanges, "Did not add symbol!");
Assert.IsTrue(receivedData, "Did not get any symbol data!");
}
[TestCase("AAPL", SecurityType.Equity)]
[TestCase("BTCUSD", SecurityType.Crypto)]
[TestCase("SPX500USD", SecurityType.Cfd)]
@@ -653,7 +721,6 @@ namespace QuantConnect.Tests.Engine.DataFeeds
public void UserDefinedUniverseSelection(string ticker, SecurityType securityType)
{
var feed = RunDataFeed();
_algorithm.SetDateTime(_manualTimeProvider.GetUtcNow());
_algorithm.SetFinishedWarmingUp();
Symbol symbol = null;
@@ -1320,9 +1387,10 @@ namespace QuantConnect.Tests.Engine.DataFeeds
Func<bool> canPerformSelection = null)
{
_algorithm.SetStartDate(_startDate);
_algorithm.SetDateTime(_manualTimeProvider.GetUtcNow());
var lastTime = _manualTimeProvider.GetUtcNow();
getNextTicksFunction = getNextTicksFunction ?? (fdqh =>
getNextTicksFunction ??= (fdqh =>
{
var time = _manualTimeProvider.GetUtcNow();
if (time == lastTime) return Enumerable.Empty<BaseData>();

View File

@@ -1,4 +1,4 @@
/*
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
@@ -19,6 +19,7 @@ using QuantConnect.Interfaces;
using System;
using System.Linq;
using QuantConnect.Configuration;
using QuantConnect.ToolBox.BitfinexDownloader;
namespace QuantConnect.Tests.ToolBox
{
@@ -53,5 +54,26 @@ namespace QuantConnect.Tests.ToolBox
Assert.AreEqual(expectedCount, tickers.Count);
}
[TestCase("--app=BFXDL --from-date=20171101-00:00:00 --tickers=1INCHUSD --resolution=Minute", 1)]
[TestCase("--app=BFXDL --from-date=20171101-00:00:00 --tickers=1INCHUSD,ADAUSDT --resolution=Minute", 2)]
[TestCase("--app=BFXDL --from-date=20171101-00:00:00 --tickers=1INCHUSD,tADAUST --resolution=Minute", 2)]
public void CanParseBitfinexTickersCorrectly(string args, int expectedCount)
{
var options = ToolboxArgumentParser.ParseArguments(args.Split(' '));
var tickers = ToolboxArgumentParser.GetTickers(options);
var downloader = new BitfinexDataDownloader();
var count = 0;
foreach (var ticker in tickers)
{
if (!string.IsNullOrEmpty(downloader.GetSymbol(ticker)))
{
count++;
}
}
Assert.AreEqual(expectedCount, count);
}
}
}

View File

@@ -97,7 +97,18 @@ namespace QuantConnect.ToolBox.BitfinexDownloader
/// <returns></returns>
internal Symbol GetSymbol(string ticker)
{
return _symbolMapper.GetLeanSymbol(ticker, SecurityType.Crypto, Market.Bitfinex);
if (_symbolMapper.IsKnownBrokerageSymbol(ticker))
{
return _symbolMapper.GetLeanSymbol(ticker, SecurityType.Crypto, Market.Bitfinex);
}
else if (SymbolPropertiesDatabase.FromDataFolder().ContainsKey(Market.Bitfinex, ticker, SecurityType.Crypto))
{
return Symbol.Create(ticker, SecurityType.Crypto, Market.Bitfinex);
}
else
{
throw new Exception($"Unknown ticker symbol: {ticker}");
}
}
#region Console Helper

View File

@@ -36,7 +36,6 @@ using QuantConnect.ToolBox.Polygon;
using QuantConnect.ToolBox.QuantQuoteConverter;
using QuantConnect.ToolBox.RandomDataGenerator;
using QuantConnect.ToolBox.YahooDownloader;
using QuantConnect.ToolBox.ZerodhaDownloader;
using QuantConnect.Util;
using System;
using System.IO;
@@ -87,10 +86,6 @@ namespace QuantConnect.ToolBox
: DateTime.UtcNow;
switch (targetApp)
{
case "zdl":
case "zerodhadownloader":
ZerodhaDataDownloaderProgram.ZerodhaDataDownloader(tickers, market, resolution, securityType, fromDate, toDate);
break;
case "gdaxdl":
case "gdaxdownloader":
GDAXDownloaderProgram.GDAXDownloader(tickers, resolution, fromDate, toDate);

View File

@@ -1,150 +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 NodaTime;
using QuantConnect.Brokerages.Zerodha;
using QuantConnect.Brokerages.Zerodha.Messages;
using QuantConnect.Configuration;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Logging;
using QuantConnect.Util;
using QuantConnect.Securities;
namespace QuantConnect.ToolBox.ZerodhaDownloader
{
public class ZerodhaDataDownloaderProgram
{
private static readonly string _apiKey = Config.Get("zerodha-api-key");
private static readonly string _accessToken = Config.Get("zerodha-access-token");
/// <summary>
/// Zerodha Historical Data Downloader Toolbox Project For LEAN Algorithmic Trading Engine.
/// By @itsbalamurali
/// </summary>
public static void ZerodhaDataDownloader(IList<string> tickers, string market, string resolution, string securityType, DateTime startDate, DateTime endDate)
{
if (resolution.IsNullOrEmpty() || tickers.IsNullOrEmpty())
{
Log.Error("ZerodhaDataDownloader ERROR: '--tickers=', --securityType, '--market' or '--resolution=' parameter is missing");
Log.Error("--tickers=eg JSWSTEEL,TCS,INFY");
Log.Error("--market=MCX/NSE/NFO/CDS/BSE");
Log.Error("--security-type=Equity/Future/Option/Commodity");
Log.Error("--resolution=Minute/Hour/Daily/Tick");
Environment.Exit(1);
}
try
{
var kite = new Kite(_apiKey, _accessToken);
var symbolMapper = new ZerodhaSymbolMapper(kite);
var castResolution = (Resolution)Enum.Parse(typeof(Resolution), resolution);
var castSecurityType = (SecurityType)Enum.Parse(typeof(SecurityType), securityType);
// Load settings from config.json and create downloader
var dataDirectory = Config.Get("data-folder", "../../../Data");
var marketHoursDatabase = MarketHoursDatabase.FromDataFolder();
foreach (var pair in tickers)
{
var zerodhaTokenList = symbolMapper.GetZerodhaInstrumentTokenList(pair);
// Download data
var pairObject = Symbol.Create(pair, castSecurityType, market);
var exchangeTimeZone = marketHoursDatabase.GetExchangeHours(market, pairObject, castSecurityType).TimeZone;
var dataTimeZone = marketHoursDatabase.GetDataTimeZone(market, pairObject, castSecurityType);
if (pairObject.ID.SecurityType != SecurityType.Forex || pairObject.ID.SecurityType != SecurityType.Cfd || pairObject.ID.SecurityType != SecurityType.Crypto || pairObject.ID.SecurityType == SecurityType.Base)
{
if (pairObject.ID.SecurityType == SecurityType.Forex || pairObject.ID.SecurityType == SecurityType.Cfd || pairObject.ID.SecurityType == SecurityType.Crypto || pairObject.ID.SecurityType == SecurityType.Base)
{
throw new ArgumentException("Invalid security type: " + pairObject.ID.SecurityType);
}
if (startDate >= endDate)
{
throw new ArgumentException("Invalid date range specified");
}
var start = startDate.ConvertTo(DateTimeZone.Utc, exchangeTimeZone);
var end = endDate.ConvertTo(DateTimeZone.Utc, exchangeTimeZone);
// Write data
var writer = new LeanDataWriter(castResolution, pairObject, dataDirectory);
IList<TradeBar> fileEnum = new List<TradeBar>();
IEnumerable<Historical> history = new List<Historical>();
var timeSpan = new TimeSpan();
switch (castResolution)
{
case Resolution.Tick:
throw new ArgumentException("Zerodha Doesn't support tick resolution");
case Resolution.Minute:
if ((end - start).Days > 60)
throw new ArgumentOutOfRangeException("For minutes data Zerodha support 60 days data download");
history = GetHistoryFromZerodha(kite, zerodhaTokenList, startDate, endDate, "minute");
timeSpan = Time.OneMinute;
break;
case Resolution.Hour:
if ((end - start).Days > 400)
throw new ArgumentOutOfRangeException("For daily data Zerodha support 400 days data download");
history = GetHistoryFromZerodha(kite, zerodhaTokenList, startDate, endDate, "60minute");
timeSpan = Time.OneHour;
break;
case Resolution.Daily:
if ((end - start).Days > 400)
throw new ArgumentOutOfRangeException("For daily data Zerodha support 400 days data download");
history = GetHistoryFromZerodha(kite, zerodhaTokenList, startDate, endDate, "day");
timeSpan = Time.OneDay;
break;
}
foreach (var bar in history)
{
var linedata = new TradeBar(bar.TimeStamp.ConvertFromUtc(dataTimeZone), pairObject, bar.Open, bar.High, bar.Low, bar.Close, bar.Volume, timeSpan);
fileEnum.Add(linedata);
}
writer.Write(fileEnum);
}
}
}
catch (Exception err)
{
Log.Error($"ZerodhaDataDownloadManager.OnError(): Message: {err.Message} Exception: {err.InnerException}");
}
}
private static IEnumerable<Historical> GetHistoryFromZerodha(Kite kite, List<uint> zerodhaTokenList, DateTime startDate, DateTime endDate, string interval)
{
var history = Enumerable.Empty<Historical>();
foreach (var token in zerodhaTokenList)
{
var tempHistory = kite.GetHistoricalData(token.ToStringInvariant(), startDate, endDate, interval);
history = history.Concat(tempHistory);
}
history = history.OrderBy(x=>x.TimeStamp);
return history;
}
}
}