Compare commits

..

31 Commits
13541 ... 13668

Author SHA1 Message Date
Adalyat Nazirov
18b99338fc Implement Binance US exchange (#6222)
* Binance.US base changes

* biniance exchange info update

* Binance US uses same fee rates as main Binance

* binance us brokerage model tests

* use base implementation of GetBuyingPowerModel method
2022-02-22 13:32:59 -03:00
Ronit Jain
f129ab1a09 Feature implement ExchangeInfoDownloader (#6213)
* add gdax exchange info downloader

* add downloader method to program]

* fetch currency description

* change definition to include headers

* use extension method to make request

* remove log from test

* replace WebRequest

* cleanup

* use relevant name

* implement IExchangeInfoDownloader for bitfinex, initial commit

* add default values

* use default market value

* use correct attribute for lotsize

* don't skip missing values

* handle multiple downloaders

* add gdax and bitfinex exchange downloader

* follow LEAN data directory structure

* update SPDB

* order tickers

* order tickers

* add exchange info downloader test template

* delete files

* update SPDB

* use currency mapping

* update bitfinex symbols

* update currency mapping

* sort result after old currency symbols are used

* use market of the respective brokerage

* no more unknown symbol

* change minimum order size value

* direct conversion possible

* update bitfinex symbols

* change user-agent

* add test for indirect conversion

* update stats
2022-02-22 13:00:54 -03:00
Ronit Jain
f86926bf7a Fixes HistoryProviderManager slice merging by using algortime for all slices (#6226)
* remove un-used

* use utc time for slice sync

* use utc time

* refactor

* add regression test

* use utc time

* use utc time

* make utctime required parameter

* add utcTime in slice creation

* assert warm up complete

* check if algorithm is still warmingup

* use exchange tz
2022-02-18 20:43:33 -03:00
Louis Szeto
68c046fc7b Update SamcoFeeModel.cs (#6220)
* Update SamcoFeeModel.cs

https://www.samco.in/charge-list-equities-and-equity-derivatives

* Update SamcoFeeModelTests.cs

Co-authored-by: Martin-Molinero <martin@quantconnect.com>
2022-02-17 20:05:31 -03:00
Alexandre Catarino
3e52816f6e Fixes GetBuyingPowerModel Method of DefaultBrokerageModel (#6215)
`CashBuyingPowerModel`, which reflected on `AlphaStreamBrokerageModel`, a margin-only brokerage.

It also didn't consider the account type, so `InteractiveBrokersBrokerageModel` was using the margin model even if `AccountType.Cash` was selected.

Removes `GetBuyingPowerModel` method from other Brokerage Models when their cases are covered by `DefaultBrokerageModel`

Fixes some typoes in `TradierBrokerageModel`

Fixes unit and regression tests. For the regression tests, we have explicitly set the brokerage model.
2022-02-17 19:41:55 -03:00
Jasper van Merle
c598a8d260 Add support for array history-provider values to QuantBook (#6219) 2022-02-17 11:24:20 -03:00
Martin-Molinero
e63bfc9127 Adjust virtual position in margin accounts (#6214)
- The BrokerTransactionHandler will also adjust virtual positions for
  margin accounts when fees are in base currency and the asset is a
  crypto or forex pair. Adding new regression tests reproducing issue
2022-02-16 15:25:47 -03:00
Martin-Molinero
e316f12394 Refactor MarginRequirementEntry. Update margins (#6210)
* Fix master

* Refactor MarginRequirementEntry to it's own file

* Update margin files

* Remove duplicate dates cases

* Update margins after fix
2022-02-15 20:24:28 -03:00
Martin-Molinero
306298a16f Update HistoryProviderManagerTests.cs 2022-02-15 12:34:00 -03:00
Adalyat Nazirov
e8160f33d5 Binance extraction (#6193)
* extract binance brokerage

* remove binance tools from main toolbox app

* modify OrderTestParameters constructor

* fix BinanceBrokerage ref config.json

* update symbol properties

* move BinanceFeeModel tests to the right place
2022-02-15 12:05:47 -03:00
Ronit Jain
ce3cb8e1a3 fix max leverage (#6207) 2022-02-14 16:42:05 -03:00
Ronit Jain
2d644d7879 Feature handle multiple history providers (#6187)
* initial commit

* get history from data providers

* merge history from history providers

* merge slice

* Add merge function in slice

* Update test

* merge aux data

* add data points in _data

* append new data points in original list

* Add test suite for HistoryProviderManager

* reduce complexity

* setup once

* add fake history provider

* doesn't count aux data

* add tests for options

* style changes

* add custom data

* merge rawDataList

* use array of history providers

* optimize

* fix formatting error

* add comment

* add tests

* simplify

* use list

* use abstraction

* split tests

* add test

* use abstraction to create generic enumertor class

* refactor

* accept list of type T

* sync history slices

* use SubscriptionDataReaderHistoryProvider for live

* rename test file

* return empty

* add tests

* cleanup

* address reviews

* optimize

* Fix method definition

* use initial time for basedata

* refactor

* re-use collection

* inherit HistoryProviderBase

* always return HistoryProviderManager

* add tests

* update rawDataList

* reset composer

* consider null elements

* add tests for binary search method

* Follow lean coding style

* revert

* remove binary search method

* convert to field
2022-02-14 11:58:00 -03:00
Colton Sellers
b80e274d4f Rename OptionsPositions.None -> Empty (#6204) 2022-02-11 19:11:30 -03:00
Martin-Molinero
9a355c9be5 Adjust INR/USD micro future scale to usd dollars (#6203) 2022-02-11 17:01:01 -03:00
Kieran Anderson
303b95ab50 Fix typo (#6201)
"buisness" --> "business"
2022-02-10 21:16:18 -03:00
Colton Sellers
d826d267f4 Update CI script with fixes for using env var (#6200) 2022-02-10 20:13:39 -03:00
Ricardo Andrés Marino Rojas
eb55311052 Feature 5157 micro futures update (#6190)
* Update MHDB and SPDB

Update MHDB and SPDB with micro futures

* Fix bugs

* Add more FutureExpiryFunctions

* Requested changes

* Nit changes

* Nit changes

- Fix some future values in SPDB
- Test LastFriday() method
- Apparently Micro CHF/USD Futures(MSF) refers to the micro of Swiss Franc Futures (6S). The same happens with Micro JPY/USD (MJY) Futures and Japanese Yen Futures (6J)

* Fix bugs

* Micro futures MultipleFactor

* Fix some futures MultipleFactor
2022-02-10 17:45:07 -03:00
Colton Sellers
27d18fa2e8 Include DataSource repos in stub generation (#6195)
* Include DataSource repos in stub generation

* Remove C# import from AlgorithmImports

* Directly import the Algorithm.CSharp namespace

* Update ci_build_stubs.sh

Case-sensitive typo

Co-authored-by: Jasper van Merle <jaspervmerle@gmail.com>

Co-authored-by: Jasper van Merle <jaspervmerle@gmail.com>
2022-02-10 15:09:08 -03:00
Adalyat Nazirov
bb0c671e7c Accept OrderSubmissionData in constructor (#6194)
* modify OrderTestParameters constructor

* add other types
2022-02-08 19:27:22 -03:00
Martin-Molinero
c8dc343c13 GetLastKnownPrices python data (#6191)
* Adding unit tests reproducing issue.

* Fix a couple of minor bugs

- IsMarketOpen will work correctly when used with daily and hourly
  resolution.
- slice.Get will work correctly with python custom data
- ExtendedDictionary will be able to dinamically access methods,
  required for python and private C# data types

* Refactor solution. Add more tests

* Remove unrequired import statement
2022-02-08 15:37:05 -03:00
Alexander Myltsev
b6815d22de Exante Brokerage initial setup (#6018)
* Exante Brokerage initial setup

* Minor exante adjustment. Address review

* Add comments to Exante config section

* Exante `GetLeverage`: handle `SecurityType.Forex`

* Exante `GetLeverage`: return 1.0 for the default case

* Cover Exante `BrokerageModel` and `FeeModel` with tests

* Add missing configurations at config.json

Fixes https://github.com/QuantConnect/Lean/pull/6018#discussion_r799527521

* Fix spaces typos

https://github.com/QuantConnect/Lean/pull/6018#discussion_r799528112

Co-authored-by: Martin-Molinero <martin@quantconnect.com>
2022-02-07 19:53:22 -03:00
Martin-Molinero
459f60603b Fees is base currency subtracted from quote currency (#6188)
* WIP

* Fix fees in base currency not being subtracted

- Fix fees in base currency not being subtracted from the quote currency
  for crypto cash accounts. Updating regression tests to assert
  portfolio, cashbook state and holdings state.

* Fix unit test race condition
2022-02-07 14:57:40 -03:00
Martin-Molinero
1aaaa20c61 Fix daily auxiliary data points emission time (#6186)
* Use GC server mode for tests

* Fix daily auxiliary data points emission time

- Due to fillforwarding, in some cases with daily resolution symbol
  change events (generically any auxiliary data) would arrive late.
  Updating regression test to reproduce the issue. Adding unit test
- Some refactoring and logging improvements

* Address reviews

* Remove old xml docs param
2022-02-04 21:06:45 -03:00
Adalyat Nazirov
07b6572bf9 Support Binance Margin trading (#6173)
* change BinanceBrokerageModel

* supply spot vs margin endpoint as parameter

* wip

* send margin order for margin account

* "NEW" means that the order has been accepted by the Binance engine.

* fix cash balance

* use JsonConverters for account parsing

* unit tests

* fixup

* lazy connect

* fix connection

* allow api client to be null if DQH only

* make method private

* more unit tests

* fix Dispose

* fix tests

* fix IsConnected condition

* run test as additional

* add some comments

* improve unit tests

* improve null checks

* tidy up the code
2022-02-04 17:30:39 -03:00
Ricardo Andrés Marino Rojas
a675aca7e5 Refactor GetFilePath() (#6164)
* Refactor `GetFilePath()`
Add also useful methods to use with this one

* Nit changes

* Requested changes

* Requested changes

* Restore SaveString()

* Nit changes

* Address self review

* Test improvements

* Adjust example KerasNeuralNetworkAlgorithm

* Minor tweak for KerasNeuralNetworkAlgorithm.py

Co-authored-by: Martin-Molinero <martin@quantconnect.com>
2022-02-04 16:58:25 -03:00
Martin-Molinero
87db3fe379 Performance improvements (#6182)
- User server mode for GC
- Remove SecurityType cache type check
2022-02-02 19:52:45 -03:00
Ricardo Andrés Marino Rojas
74321d1727 Update CME future holidays and early closes in MHDB (#6181)
* CME Futures update in MHDB

* Fix indent spaces nit errors

* Revert "Fix indent spaces nit errors"

This reverts commit 6ebb8614c0.

* Fix tabs errors

* Update MHDB with 2022 holidays

* Fix bugs

* Remove earlyOpens
2022-02-02 19:28:21 -03:00
Martin-Molinero
9fd50a302e Update or remove SharpZipLib dependency (#6180) 2022-02-02 13:28:24 -03:00
Martin-Molinero
fc0b2f3fa4 Fix for Add & Remove option contract case (#6172) 2022-01-28 14:13:21 -03:00
Martin-Molinero
c4a2d6eef4 Crypto base currency fees handled correctly (#6166)
* Binance fees deducted from fill quantity accordengly

- For Binance cash accounts while buying, if fees are from the base
  currency of leans virtual position, we need to deduct the fee from the
  fill quantity, else we can end with a position bigger that it actually
  is and not be able to liquidate

* Refactor solution

- Refactor solution into a more generic approach solving fees in base
  currency at the BrokerageTransactionHandler level, covering all
  brokerages that require it. Adding regression algorithm reproducing
  issue.
- Update Bitfinex and Binance fee models to correctly reflact reality

* Log fill quantity adjusment once
2022-01-28 13:02:58 -03:00
Martin-Molinero
c2b60e4e48 Fix empty parameter set deserialization (#6171)
- Fix empty ParameterSet deserialization. Adding unit test
2022-01-27 20:18:47 -03:00
343 changed files with 35846 additions and 6119 deletions

View File

@@ -28,3 +28,5 @@ jobs:
./ci_build_stubs.sh -t -g -p
env:
PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
ADDITIONAL_STUBS_REPOS: ${{ secrets.ADDITIONAL_STUBS_REPOS }}
QC_GIT_TOKEN: ${{ secrets.QC_GIT_TOKEN }}

View File

@@ -0,0 +1,150 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using System.Collections.Generic;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Regression algorithm reproducing issue where underlying option contract would be removed with the first call
/// too RemoveOptionContract
/// </summary>
public class AddTwoAndRemoveOneOptionContractRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol _contract1;
private Symbol _contract2;
private bool _hasRemoved;
public override void Initialize()
{
SetStartDate(2014, 06, 06);
SetEndDate(2014, 06, 06);
UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw;
UniverseSettings.MinimumTimeInUniverse = TimeSpan.Zero;
var aapl = QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA);
var contracts = OptionChainProvider.GetOptionContractList(aapl, Time)
.OrderBy(symbol => symbol.ID.Symbol)
.Where(optionContract => optionContract.ID.OptionRight == OptionRight.Call
&& optionContract.ID.OptionStyle == OptionStyle.American)
.Take(2)
.ToList();
_contract1 = contracts[0];
_contract2 = contracts[1];
AddOptionContract(_contract1);
AddOptionContract(_contract2);
}
public override void OnData(Slice slice)
{
if (slice.HasData)
{
if (!_hasRemoved)
{
RemoveOptionContract(_contract1);
_hasRemoved = true;
}
else
{
var subscriptions =
SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs("AAPL");
if (subscriptions.Count == 0)
{
throw new Exception("No configuration for underlying was found!");
}
if (!Portfolio.Invested)
{
Buy(_contract2, 1);
}
}
}
}
public override void OnEndOfAlgorithm()
{
if (!_hasRemoved)
{
throw new Exception("Expect a single call to OnData where we removed the option and underlying");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "2"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0%"},
{"Drawdown", "0%"},
{"Expectancy", "0"},
{"Net Profit", "0%"},
{"Sharpe Ratio", "0"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0"},
{"Beta", "0"},
{"Annual Standard Deviation", "0"},
{"Annual Variance", "0"},
{"Information Ratio", "0"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$2.00"},
{"Estimated Strategy Capacity", "$230000.00"},
{"Lowest Capacity Asset", "AAPL VXBK4QQIRLZA|AAPL R735QTJ8XC9X"},
{"Fitness Score", "0"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "0"},
{"Return Over Maximum Drawdown", "0"},
{"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", "228194dcc6fd8689a67f383577ee2d85"}
};
}
}

View File

@@ -14,6 +14,7 @@
*/
using System.Collections.Generic;
using QuantConnect.Brokerages;
using QuantConnect.Data;
using QuantConnect.Interfaces;
@@ -33,6 +34,7 @@ namespace QuantConnect.Algorithm.CSharp
{
SetStartDate(2018, 04, 04); //Set Start Date
SetEndDate(2018, 04, 04); //Set End Date
SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash);
//Before setting any cash or adding a Security call SetAccountCurrency
SetAccountCurrency("EUR");
SetCash(100000); //Set Strategy Cash
@@ -87,14 +89,14 @@ namespace QuantConnect.Algorithm.CSharp
{"Information Ratio", "0"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$0.00"},
{"Total Fees", "$298.35"},
{"Estimated Strategy Capacity", "$85000.00"},
{"Lowest Capacity Asset", "BTCEUR XJ"},
{"Fitness Score", "0.506"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "-14.148"},
{"Return Over Maximum Drawdown", "-13.614"},
{"Portfolio Turnover", "1.073"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
@@ -109,7 +111,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "18dc611407abec4ea47092e71f33f983"}
{"OrderListHash", "2ba443899dcccc79dc0f04441f797bf9"}
};
}
}

View File

@@ -13,6 +13,7 @@
* limitations under the License.
*/
using System;
using QuantConnect.Data;
using QuantConnect.Orders;
using QuantConnect.Interfaces;
@@ -62,7 +63,11 @@ namespace QuantConnect.Algorithm.CSharp
{
foreach (var changedEvent in data.SymbolChangedEvents.Values)
{
Log($"{Time} - SymbolChanged event: {changedEvent}");
Debug($"{Time} - SymbolChanged event: {changedEvent}");
if (Time.TimeOfDay != TimeSpan.Zero)
{
throw new Exception($"{Time} unexpected symbol changed event {changedEvent}!");
}
}
if (!Portfolio.Invested)

View File

@@ -76,6 +76,15 @@ namespace QuantConnect.Algorithm.CSharp
/// <param name="slice">The current slice of data keyed by symbol string</param>
public override void OnData(Slice slice)
{
foreach (var changedEvent in slice.SymbolChangedEvents.Values)
{
Debug($"{Time} - SymbolChanged event: {changedEvent}");
if (Time.TimeOfDay != TimeSpan.Zero)
{
throw new Exception($"{Time} unexpected symbol changed event {changedEvent}!");
}
}
if (!Portfolio.Invested)
{
foreach(var chain in slice.FutureChains)

View File

@@ -46,7 +46,7 @@ namespace QuantConnect.Algorithm.CSharp
public override void Initialize()
{
SetStartDate(2013, 10, 08);
SetEndDate(2013, 10, 10);
SetEndDate(2014, 10, 10);
SetCash(1000000);
var futureSP500 = AddFuture(RootSP500, Resolution);
@@ -77,7 +77,7 @@ namespace QuantConnect.Algorithm.CSharp
).FirstOrDefault();
// if found, trade it
if (contract != null)
if (contract != null && IsMarketOpen(contract.Symbol))
{
_contractSymbol = contract.Symbol;
MarketOrder(_contractSymbol, 1);
@@ -88,6 +88,14 @@ namespace QuantConnect.Algorithm.CSharp
{
Liquidate();
}
foreach (var changedEvent in slice.SymbolChangedEvents.Values)
{
if (Time.TimeOfDay != TimeSpan.Zero)
{
throw new Exception($"{Time} unexpected symbol changed event {changedEvent}!");
}
}
}
/// <summary>
@@ -105,34 +113,34 @@ namespace QuantConnect.Algorithm.CSharp
/// </summary>
public virtual Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "6"},
{"Average Win", "0%"},
{"Average Loss", "-0.10%"},
{"Compounding Annual Return", "-23.119%"},
{"Drawdown", "0.300%"},
{"Expectancy", "-1"},
{"Net Profit", "-0.276%"},
{"Sharpe Ratio", "-13.736"},
{"Total Trades", "92"},
{"Average Win", "0.08%"},
{"Average Loss", "-0.01%"},
{"Compounding Annual Return", "-0.450%"},
{"Drawdown", "0.500%"},
{"Expectancy", "-0.824"},
{"Net Profit", "-0.453%"},
{"Sharpe Ratio", "-1.803"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "100%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "-0.526"},
{"Beta", "0.057"},
{"Annual Standard Deviation", "0.015"},
{"Loss Rate", "98%"},
{"Win Rate", "2%"},
{"Profit-Loss Ratio", "7.09"},
{"Alpha", "-0.003"},
{"Beta", "-0.001"},
{"Annual Standard Deviation", "0.002"},
{"Annual Variance", "0"},
{"Information Ratio", "-31.088"},
{"Tracking Error", "0.189"},
{"Treynor Ratio", "-3.51"},
{"Total Fees", "$11.10"},
{"Estimated Strategy Capacity", "$200000000.00"},
{"Lowest Capacity Asset", "GC VOFJUCDY9XNH"},
{"Fitness Score", "0"},
{"Information Ratio", "-1.394"},
{"Tracking Error", "0.089"},
{"Treynor Ratio", "4.298"},
{"Total Fees", "$170.20"},
{"Estimated Strategy Capacity", "$36000.00"},
{"Lowest Capacity Asset", "ES VP274HSU1AF5"},
{"Fitness Score", "0.009"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "-17.118"},
{"Return Over Maximum Drawdown", "-83.844"},
{"Portfolio Turnover", "0.16"},
{"Sortino Ratio", "-0.8"},
{"Return Over Maximum Drawdown", "-0.992"},
{"Portfolio Turnover", "0.025"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
@@ -146,7 +154,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "512f55519e5221c7e82e1d9f5ddd1b9f"}
{"OrderListHash", "09b2f274fa2385597a803e58b784f675"}
};
}
}

View File

@@ -49,34 +49,34 @@ namespace QuantConnect.Algorithm.CSharp
/// </summary>
public override Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "140"},
{"Total Trades", "1988"},
{"Average Win", "0.01%"},
{"Average Loss", "-0.02%"},
{"Compounding Annual Return", "-38.171%"},
{"Drawdown", "0.400%"},
{"Expectancy", "-0.369"},
{"Net Profit", "-0.394%"},
{"Sharpe Ratio", "-24.82"},
{"Average Loss", "0.00%"},
{"Compounding Annual Return", "-4.120%"},
{"Drawdown", "4.200%"},
{"Expectancy", "-0.870"},
{"Net Profit", "-4.150%"},
{"Sharpe Ratio", "-6.061"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "66%"},
{"Win Rate", "34%"},
{"Profit-Loss Ratio", "0.84"},
{"Alpha", "0.42"},
{"Beta", "-0.041"},
{"Annual Standard Deviation", "0.01"},
{"Loss Rate", "97%"},
{"Win Rate", "3%"},
{"Profit-Loss Ratio", "2.92"},
{"Alpha", "-0.027"},
{"Beta", "-0.006"},
{"Annual Standard Deviation", "0.005"},
{"Annual Variance", "0"},
{"Information Ratio", "-65.112"},
{"Tracking Error", "0.253"},
{"Treynor Ratio", "6.024"},
{"Total Fees", "$259.00"},
{"Estimated Strategy Capacity", "$130000.00"},
{"Lowest Capacity Asset", "GC VOFJUCDY9XNH"},
{"Fitness Score", "0"},
{"Information Ratio", "-1.66"},
{"Tracking Error", "0.089"},
{"Treynor Ratio", "4.919"},
{"Total Fees", "$3677.80"},
{"Estimated Strategy Capacity", "$2000.00"},
{"Lowest Capacity Asset", "ES VP274HSU1AF5"},
{"Fitness Score", "0.128"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "-43.422"},
{"Return Over Maximum Drawdown", "-100.459"},
{"Portfolio Turnover", "4.716"},
{"Sortino Ratio", "-6.856"},
{"Return Over Maximum Drawdown", "-0.995"},
{"Portfolio Turnover", "0.648"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
@@ -90,7 +90,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "320067074c8dd771f69602ab07001f1e"}
{"OrderListHash", "87d2b127c9859cad9d2c65ac9d76deb5"}
};
}
}

View File

@@ -69,7 +69,7 @@ namespace QuantConnect.Algorithm.CSharp
var contractsByExpiration = chain.Where(x => x.Expiry != Time.Date).OrderBy(x => x.Expiry);
var contract = contractsByExpiration.FirstOrDefault();
if (contract != null)
if (contract != null && IsMarketOpen(contract.Symbol))
{
// if found, trade it
MarketOrder(contract.Symbol, 1);

View File

@@ -76,7 +76,7 @@ namespace QuantConnect.Algorithm.CSharp
.ThenByDescending(x => x.Right)
.FirstOrDefault();
if (atmContract != null)
if (atmContract != null && IsMarketOpen(atmContract.Symbol))
{
// if found, trade it
MarketOrder(atmContract.Symbol, 1);

View File

@@ -0,0 +1,90 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using QuantConnect.Brokerages;
using System.Collections.Generic;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Binance cash account regression algorithm, reproduces issue https://github.com/QuantConnect/Lean/issues/6123
/// </summary>
public class BinanceCashAccountFeeRegressionAlgorithm : CryptoBaseCurrencyFeeRegressionAlgorithm
{
/// <summary>
/// The target account type
/// </summary>
protected override AccountType AccountType { get; } = AccountType.Cash;
public override void Initialize()
{
SetAccountCurrency("USDT");
SetStartDate(2018, 05, 02);
SetEndDate(2018, 05, 03);
BrokerageName = BrokerageName.Binance;
Pair = "BTCUSDT";
base.Initialize();
}
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public override Dictionary<string, string> ExpectedStatistics => new()
{
{"Total Trades", "49"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0%"},
{"Drawdown", "0%"},
{"Expectancy", "0"},
{"Net Profit", "0%"},
{"Sharpe Ratio", "0"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0"},
{"Beta", "0"},
{"Annual Standard Deviation", "0"},
{"Annual Variance", "0"},
{"Information Ratio", "0"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$45.62"},
{"Estimated Strategy Capacity", "$220000.00"},
{"Lowest Capacity Asset", "BTCUSDT 18N"},
{"Fitness Score", "0.208"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "26.189"},
{"Portfolio Turnover", "0.208"},
{"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", "USDT0"},
{"Total Accumulated Estimated Alpha Value", "USDT0"},
{"Mean Population Estimated Insight Value", "USDT0"},
{"Mean Population Direction", "0%"},
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "7417649395922ff3791471b4f3b5c021"}
};
}
}

View File

@@ -0,0 +1,90 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using QuantConnect.Brokerages;
using System.Collections.Generic;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Binance margin account regression algorithm, reproduces issue https://github.com/QuantConnect/Lean/issues/6123
/// </summary>
public class BinanceMarginAccountFeeRegressionAlgorithm : CryptoBaseCurrencyFeeRegressionAlgorithm
{
/// <summary>
/// The target account type
/// </summary>
protected override AccountType AccountType { get; } = AccountType.Margin;
public override void Initialize()
{
SetAccountCurrency("USDT");
SetStartDate(2018, 05, 02);
SetEndDate(2018, 05, 03);
BrokerageName = BrokerageName.Binance;
Pair = "BTCUSDT";
base.Initialize();
}
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public override Dictionary<string, string> ExpectedStatistics => new()
{
{"Total Trades", "49"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0%"},
{"Drawdown", "0%"},
{"Expectancy", "0"},
{"Net Profit", "0%"},
{"Sharpe Ratio", "0"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0"},
{"Beta", "0"},
{"Annual Standard Deviation", "0"},
{"Annual Variance", "0"},
{"Information Ratio", "0"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$45.62"},
{"Estimated Strategy Capacity", "$12000000.00"},
{"Lowest Capacity Asset", "BTCUSDT 18N"},
{"Fitness Score", "0.208"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "26.189"},
{"Portfolio Turnover", "0.208"},
{"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", "USDT0"},
{"Total Accumulated Estimated Alpha Value", "USDT0"},
{"Mean Population Estimated Insight Value", "USDT0"},
{"Mean Population Direction", "0%"},
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "7417649395922ff3791471b4f3b5c021"}
};
}
}

View File

@@ -0,0 +1,89 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using QuantConnect.Brokerages;
using System.Collections.Generic;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Bitfinex cash account regression algorithm, reproduces issue https://github.com/QuantConnect/Lean/issues/6123
/// </summary>
public class BitfinexCashAccountFeeRegressionAlgorithm : CryptoBaseCurrencyFeeRegressionAlgorithm
{
/// <summary>
/// The target account type
/// </summary>
protected override AccountType AccountType { get; } = AccountType.Cash;
public override void Initialize()
{
SetStartDate(2013, 10, 02);
SetEndDate(2013, 10, 03);
BrokerageName = BrokerageName.Bitfinex;
Pair = "BTCUSD";
base.Initialize();
}
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public override Dictionary<string, string> ExpectedStatistics => new()
{
{"Total Trades", "49"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0%"},
{"Drawdown", "0%"},
{"Expectancy", "0"},
{"Net Profit", "0%"},
{"Sharpe Ratio", "0"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0"},
{"Beta", "0"},
{"Annual Standard Deviation", "0"},
{"Annual Variance", "0"},
{"Information Ratio", "0"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$1.13"},
{"Estimated Strategy Capacity", "$2000.00"},
{"Lowest Capacity Asset", "BTCUSD E3"},
{"Fitness Score", "0.002"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
{"Portfolio Turnover", "0.002"},
{"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", "7f892f0c42d8826ff770ee602fe207a2"}
};
}
}

View File

@@ -0,0 +1,89 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using QuantConnect.Brokerages;
using System.Collections.Generic;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Bitfinex margin account regression algorithm, reproduces issue https://github.com/QuantConnect/Lean/issues/6123
/// </summary>
public class BitfinexMarginAccountFeeRegressionAlgorithm : CryptoBaseCurrencyFeeRegressionAlgorithm
{
/// <summary>
/// The target account type
/// </summary>
protected override AccountType AccountType { get; } = AccountType.Margin;
public override void Initialize()
{
SetStartDate(2013, 10, 02);
SetEndDate(2013, 10, 03);
BrokerageName = BrokerageName.Bitfinex;
Pair = "BTCUSD";
base.Initialize();
}
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public override Dictionary<string, string> ExpectedStatistics => new()
{
{"Total Trades", "49"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0%"},
{"Drawdown", "0%"},
{"Expectancy", "0"},
{"Net Profit", "0%"},
{"Sharpe Ratio", "0"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0"},
{"Beta", "0"},
{"Annual Standard Deviation", "0"},
{"Annual Variance", "0"},
{"Information Ratio", "0"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$1.13"},
{"Estimated Strategy Capacity", "$640000.00"},
{"Lowest Capacity Asset", "BTCUSD E3"},
{"Fitness Score", "0.002"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
{"Portfolio Turnover", "0.002"},
{"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", "7f892f0c42d8826ff770ee602fe207a2"}
};
}
}

View File

@@ -0,0 +1,110 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Linq;
using QuantConnect.Util;
using QuantConnect.Data;
using QuantConnect.Orders;
using QuantConnect.Interfaces;
using QuantConnect.Brokerages;
using System.Collections.Generic;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Base crypto account regression algorithm trading in and out
/// </summary>
public abstract class CryptoBaseCurrencyFeeRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol _symbol;
/// <summary>
/// The target account type
/// </summary>
protected abstract AccountType AccountType { get; }
/// <summary>
/// The target brokerage model name
/// </summary>
protected BrokerageName BrokerageName { get; set; }
/// <summary>
/// The pair to add and trade
/// </summary>
protected string Pair { get; set; }
/// <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()
{
SetBrokerageModel(BrokerageName, AccountType);
_symbol = AddCrypto(Pair, Resolution.Hour).Symbol;
}
/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// </summary>
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
public override void OnData(Slice data)
{
if (!Portfolio.Invested)
{
CurrencyPairUtil.DecomposeCurrencyPair(_symbol, out var baseCurrency, out var quoteCurrency);
var initialQuoteCurrency = Portfolio.CashBook[quoteCurrency].Amount;
var ticket = Buy(_symbol, 0.1m);
var filledEvent = ticket.OrderEvents.Single(orderEvent => orderEvent.Status == OrderStatus.Filled);
if (Portfolio.CashBook[baseCurrency].Amount != ticket.QuantityFilled
|| filledEvent.FillQuantity != ticket.QuantityFilled
|| (0.1m - filledEvent.OrderFee.Value.Amount) != ticket.QuantityFilled)
{
throw new Exception($"Unexpected BaseCurrency porfoltio status. Event {filledEvent}. CashBook: {Portfolio.CashBook}. ");
}
if (Portfolio.CashBook[quoteCurrency].Amount != (initialQuoteCurrency - 0.1m * filledEvent.FillPrice))
{
throw new Exception($"Unexpected QuoteCurrency porfoltio status. Event {filledEvent}. CashBook: {Portfolio.CashBook}. ");
}
if (Securities[_symbol].Holdings.Quantity != (0.1m - filledEvent.OrderFee.Value.Amount))
{
throw new Exception($"Unexpected Holdings: {Securities[_symbol].Holdings}. Event {filledEvent}");
}
}
else
{
Liquidate();
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public abstract Dictionary<string, string> ExpectedStatistics { get; }
}
}

View File

@@ -18,7 +18,9 @@ using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using QuantConnect.Data;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
@@ -31,12 +33,14 @@ namespace QuantConnect.Algorithm.CSharp
/// <meta name="tag" content="regression test" />
public class CustomDataRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private bool _warmedUpChecked = false;
/// <summary>
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
/// </summary>
public override void Initialize()
{
SetStartDate(2011, 9, 13);
SetStartDate(2011, 9, 14);
SetEndDate(2015, 12, 01);
//Set the cash for the strategy:
@@ -45,6 +49,9 @@ namespace QuantConnect.Algorithm.CSharp
//Define the symbol and "type" of our generic data:
var resolution = LiveMode ? Resolution.Second : Resolution.Daily;
AddData<Bitcoin>("BTC", resolution);
var seeder = new FuncSecuritySeeder(GetLastKnownPrices);
SetSecurityInitializer(security => seeder.SeedSecurity(security));
}
/// <summary>
@@ -66,6 +73,30 @@ namespace QuantConnect.Algorithm.CSharp
}
}
public override void OnSecuritiesChanged(SecurityChanges changes)
{
changes.FilterCustomSecurities = false;
foreach (var addedSecurity in changes.AddedSecurities)
{
if (addedSecurity.Symbol.Value == "BTC")
{
_warmedUpChecked = true;
}
if (!addedSecurity.HasData)
{
throw new Exception($"Security {addedSecurity.Symbol} was not warmed up!");
}
}
}
public override void OnEndOfAlgorithm()
{
if (!_warmedUpChecked)
{
throw new Exception($"Security was not warmed up!");
}
}
/// <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>
@@ -84,30 +115,30 @@ namespace QuantConnect.Algorithm.CSharp
{"Total Trades", "1"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "157.497%"},
{"Compounding Annual Return", "157.655%"},
{"Drawdown", "84.800%"},
{"Expectancy", "0"},
{"Net Profit", "5319.007%"},
{"Sharpe Ratio", "2.086"},
{"Probabilistic Sharpe Ratio", "69.456%"},
{"Sharpe Ratio", "2.123"},
{"Probabilistic Sharpe Ratio", "70.581%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "1.747"},
{"Beta", "0.047"},
{"Alpha", "1.776"},
{"Beta", "0.059"},
{"Annual Standard Deviation", "0.84"},
{"Annual Variance", "0.706"},
{"Information Ratio", "1.922"},
{"Tracking Error", "0.848"},
{"Treynor Ratio", "37.473"},
{"Information Ratio", "1.962"},
{"Tracking Error", "0.847"},
{"Treynor Ratio", "30.455"},
{"Total Fees", "$0.00"},
{"Estimated Strategy Capacity", "$0"},
{"Lowest Capacity Asset", "BTC.Bitcoin 2S"},
{"Fitness Score", "0"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "2.269"},
{"Return Over Maximum Drawdown", "1.858"},
{"Sortino Ratio", "2.271"},
{"Return Over Maximum Drawdown", "1.86"},
{"Portfolio Turnover", "0"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},

View File

@@ -0,0 +1,120 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using QuantConnect.Data;
using QuantConnect.Interfaces;
using System;
using System.Collections.Generic;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Test algorithm to verify the corret working of <see cref="HistoryProviderManager"/>
/// </summary>
public class HistoryProviderManagerRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private bool _onDataTriggered = new();
/// <summary>
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
/// </summary>
public override void Initialize()
{
SetStartDate(2017, 12, 17);
AddCrypto("BTCUSD");
SetWarmup(1000000);
}
/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// </summary>
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
public override void OnData(Slice data)
{
_onDataTriggered = true;
}
public override void OnEndOfAlgorithm()
{
if (IsWarmingUp)
{
throw new Exception("Warm up not complete");
}
if (!_onDataTriggered)
{
throw new Exception("No data received is OnData method");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "0"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0%"},
{"Drawdown", "0%"},
{"Expectancy", "0"},
{"Net Profit", "0%"},
{"Sharpe Ratio", "0"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0"},
{"Beta", "0"},
{"Annual Standard Deviation", "0"},
{"Annual Variance", "0"},
{"Information Ratio", "-0.56"},
{"Tracking Error", "0.164"},
{"Treynor Ratio", "0"},
{"Total Fees", "$0.00"},
{"Estimated Strategy Capacity", "$0"},
{"Lowest Capacity Asset", ""},
{"Fitness Score", "0"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
{"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", "d41d8cd98f00b204e9800998ecf8427e"}
};
}
}

View File

@@ -1,4 +1,4 @@
/*
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
@@ -18,6 +18,7 @@ using QuantConnect.Indicators;
using QuantConnect.Interfaces;
using QuantConnect.Storage;
using System;
using System.Collections.Generic;
using System.Linq;
namespace QuantConnect.Algorithm.CSharp
@@ -29,7 +30,7 @@ namespace QuantConnect.Algorithm.CSharp
/// history call. This pattern can be equally applied to a machine learning model being
/// trained and then saving the model weights in the object store.
/// </summary>
public class ObjectStoreExampleAlgorithm : QCAlgorithm
public class ObjectStoreExampleAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private const string SPY_Close_ObjectStore_Key = "spy_close";
private Symbol SPY;
@@ -127,5 +128,64 @@ namespace QuantConnect.Algorithm.CSharp
}
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "1"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "271.453%"},
{"Drawdown", "2.200%"},
{"Expectancy", "0"},
{"Net Profit", "1.692%"},
{"Sharpe Ratio", "8.888"},
{"Probabilistic Sharpe Ratio", "67.609%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "-0.005"},
{"Beta", "0.996"},
{"Annual Standard Deviation", "0.222"},
{"Annual Variance", "0.049"},
{"Information Ratio", "-14.565"},
{"Tracking Error", "0.001"},
{"Treynor Ratio", "1.978"},
{"Total Fees", "$3.44"},
{"Estimated Strategy Capacity", "$56000000.00"},
{"Lowest Capacity Asset", "SPY R735QTJ8XC9X"},
{"Fitness Score", "0.248"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "93.728"},
{"Portfolio Turnover", "0.248"},
{"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", "9e4bfd2eb0b81ee5bc1b197a87ccedbe"}
};
}
}

View File

@@ -16,6 +16,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Brokerages;
using QuantConnect.Data;
using QuantConnect.Interfaces;
@@ -33,6 +34,7 @@ namespace QuantConnect.Algorithm.CSharp
{
SetStartDate(2018, 04, 04);
SetEndDate(2018, 04, 04);
SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash);
// GDAX doesn't have LTCETH or ETHLTC, but they do have ETHUSD and LTCUSD to form a path between ETH and LTC
SetAccountCurrency("ETH");

View File

@@ -15,6 +15,7 @@
using System;
using System.Collections.Generic;
using QuantConnect.Brokerages;
using QuantConnect.Data;
using QuantConnect.Interfaces;
@@ -32,6 +33,7 @@ namespace QuantConnect.Algorithm.CSharp
{
SetStartDate(2018, 4, 5);
SetEndDate(2018, 4, 5);
SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash);
SetCash(10000);
SetWarmUp(TimeSpan.FromDays(1));
@@ -100,14 +102,14 @@ namespace QuantConnect.Algorithm.CSharp
{"Information Ratio", "0"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$0.00"},
{"Total Fees", "$29.84"},
{"Estimated Strategy Capacity", "$410000.00"},
{"Lowest Capacity Asset", "LTCUSD XJ"},
{"Fitness Score", "0.499"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "-315.48"},
{"Return Over Maximum Drawdown", "-189.336"},
{"Portfolio Turnover", "0.999"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
@@ -122,7 +124,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "d38f5aec6a9c4dcf62de7b0dff26117f"}
{"OrderListHash", "c764c98687300a2da250436baae2963c"}
};
}
}

View File

@@ -22,6 +22,7 @@ class BasicSetAccountCurrencyAlgorithm(QCAlgorithm):
self.SetStartDate(2018, 4, 4) #Set Start Date
self.SetEndDate(2018, 4, 4) #Set End Date
self.SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash);
# Before setting any cash or adding a Security call SetAccountCurrency
self.SetAccountCurrency("EUR")
self.SetCash(100000) #Set Strategy Cash

View File

@@ -23,7 +23,7 @@ class BasicTemplateFuturesDailyAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2013, 10, 8)
self.SetEndDate(2013, 10, 10)
self.SetEndDate(2014, 10, 10)
self.SetCash(1000000)
self.contractSymbol = None
@@ -50,6 +50,7 @@ class BasicTemplateFuturesDailyAlgorithm(QCAlgorithm):
front = sorted(contracts, key = lambda x: x.Expiry, reverse=True)[0]
self.contractSymbol = front.Symbol
self.MarketOrder(front.Symbol , 1)
if self.IsMarketOpen(self.contractSymbol):
self.MarketOrder(front.Symbol , 1)
else:
self.Liquidate()

View File

@@ -23,7 +23,7 @@ class BasicTemplateFuturesHourlyAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2013, 10, 8)
self.SetEndDate(2013, 10, 10)
self.SetEndDate(2014, 10, 10)
self.SetCash(1000000)
self.contractSymbol = None
@@ -50,6 +50,7 @@ class BasicTemplateFuturesHourlyAlgorithm(QCAlgorithm):
front = sorted(contracts, key = lambda x: x.Expiry, reverse=True)[0]
self.contractSymbol = front.Symbol
self.MarketOrder(front.Symbol , 1)
if self.IsMarketOpen(self.contractSymbol):
self.MarketOrder(front.Symbol , 1)
else:
self.Liquidate()

View File

@@ -51,7 +51,7 @@ class BasicTemplateOptionsDailyAlgorithm(QCAlgorithm):
contracts = sorted(chain, key = lambda x: x.Expiry)
# if found, trade it
if len(contracts) == 0: return
if len(contracts) == 0 or not self.IsMarketOpen(contracts[0].Symbol): return
symbol = contracts[0].Symbol
self.MarketOrder(symbol, 1)

View File

@@ -57,7 +57,7 @@ class BasicTemplateOptionsHourlyAlgorithm(QCAlgorithm):
key = lambda x: x.Right, reverse=True)
# if found, trade it
if len(contracts) == 0: return
if len(contracts) == 0 or not self.IsMarketOpen(contracts[0].Symbol): return
symbol = contracts[0].Symbol
self.MarketOrder(symbol, 1)
self.MarketOnCloseOrder(symbol, -1)

View File

@@ -25,18 +25,33 @@ class CustomDataRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2011,9,13) # Set Start Date
self.SetStartDate(2011,9,14) # Set Start Date
self.SetEndDate(2015,12,1) # Set End Date
self.SetCash(100000) # Set Strategy Cash
resolution = Resolution.Second if self.LiveMode else Resolution.Daily
self.AddData(Bitcoin, "BTC", resolution)
seeder = FuncSecuritySeeder(self.GetLastKnownPrices)
self.SetSecurityInitializer(lambda x: seeder.SeedSecurity(x))
self._warmedUpChecked = False
def OnData(self, data):
if not self.Portfolio.Invested:
if data['BTC'].Close != 0 :
self.Order('BTC', self.Portfolio.MarginRemaining/abs(data['BTC'].Close + 1))
def OnSecuritiesChanged(self, changes):
changes.FilterCustomSecurities = False
for addedSecurity in changes.AddedSecurities:
if addedSecurity.Symbol.Value == "BTC":
self._warmedUpChecked = True
if not addedSecurity.HasData:
raise ValueError(f"Security {addedSecurity.Symbol} was not warmed up!")
def OnEndOfAlgorithm(self):
if not self._warmedUpChecked:
raise ValueError("Security was not warmed up!")
class Bitcoin(PythonData):
'''Custom Data Type: Bitcoin data from Quandl - http://www.quandl.com/help/api-for-bitcoin-data'''

View File

@@ -12,6 +12,7 @@
# limitations under the License.
from AlgorithmImports import *
from QuantConnect.Algorithm.CSharp import *
### <summary>
### Basic template algorithm simply initializes the date range and cash. This is a skeleton

View File

@@ -13,11 +13,10 @@
from AlgorithmImports import *
from io import StringIO
from keras.models import Sequential
from keras.models import *
from tensorflow import keras
from keras.layers import Dense, Activation
from keras.optimizers import SGD
from keras.utils.generic_utils import serialize_keras_object
class KerasNeuralNetworkAlgorithm(QCAlgorithm):
@@ -32,11 +31,13 @@ class KerasNeuralNetworkAlgorithm(QCAlgorithm):
symbol = self.AddEquity(ticker).Symbol
# Read the model saved in the ObjectStore
if self.ObjectStore.ContainsKey(f'{symbol}_model'):
modelStr = self.ObjectStore.Read(f'{symbol}_model')
config = json.loads(modelStr)['config']
self.modelBySymbol[symbol] = Sequential.from_config(config)
self.Debug(f'Model for {symbol} sucessfully retrieved from the ObjectStore')
for kvp in self.ObjectStore:
key = f'{symbol}_model'
if not key == kvp.Key or kvp.Value is None:
continue
filePath = self.ObjectStore.GetFilePath(kvp.Key)
self.modelBySymbol[symbol] = keras.models.load_model(filePath)
self.Debug(f'Model for {symbol} sucessfully retrieved. File {filePath}. Size {kvp.Value.Length}. Weights {self.modelBySymbol[symbol].get_weights()}')
# Look-back period for training set
self.lookback = 30
@@ -51,19 +52,21 @@ class KerasNeuralNetworkAlgorithm(QCAlgorithm):
self.Schedule.On(
self.DateRules.EveryDay("SPY"),
self.TimeRules.AfterMarketOpen("SPY", 30),
self.Trade)
self.Trade)
def OnEndOfAlgorithm(self):
''' Save the data and the mode using the ObjectStore '''
for symbol, model in self.modelBySymbol.items():
modelStr = json.dumps(serialize_keras_object(model))
self.ObjectStore.Save(f'{symbol}_model', modelStr)
key = f'{symbol}_model'
file = self.ObjectStore.GetFilePath(key)
model.save(file)
self.ObjectStore.Save(key)
self.Debug(f'Model for {symbol} sucessfully saved in the ObjectStore')
def NeuralNetworkTraining(self):
'''Train the Neural Network and save the model in the ObjectStore'''
'''Train the Neural Network and save the model in the ObjectStore'''
symbols = self.Securities.keys()
# Daily historical data is used to train the machine learning model
@@ -89,19 +92,18 @@ class KerasNeuralNetworkAlgorithm(QCAlgorithm):
# choose loss function and optimizing method
model.compile(loss='mse', optimizer=sgd)
# pick an iteration number large enough for convergence
# pick an iteration number large enough for convergence
for step in range(200):
# training the model
cost = model.train_on_batch(predictor, predictand)
self.modelBySymbol[symbol] = model
def Trade(self):
'''
Predict the price using the trained model and out-of-sample data
Enter or exit positions based on relationship of the open price of the current bar and the prices defined by the machine learning model.
Liquidate if the open price is below the sell price and buy if the open price is above the buy price
Liquidate if the open price is below the sell price and buy if the open price is above the buy price
'''
target = 1 / len(self.Securities)

View File

@@ -20,7 +20,7 @@ class TwoLegCurrencyConversionRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2018, 4, 4)
self.SetEndDate(2018, 4, 4)
self.SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash);
# GDAX doesn't have LTCETH or ETHLTC, but they do have ETHUSD and LTCUSD to form a path between ETH and LTC
self.SetAccountCurrency("ETH")
self.SetCash("ETH", 100000)

View File

@@ -1269,13 +1269,11 @@ namespace QuantConnect.Algorithm
[DocumentationAttribute(SecuritiesAndPortfolio)]
public bool IsMarketOpen(Symbol symbol)
{
var exchangeHours = MarketHoursDatabase
.FromDataFolder()
.GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType);
var time = UtcTime.ConvertFromUtc(exchangeHours.TimeZone);
return exchangeHours.IsOpen(time, false);
if (Securities.TryGetValue(symbol, out var security))
{
return security.IsMarketOpen(false);
}
return symbol.IsMarketOpen(UtcTime, false);
}
private SubmitOrderRequest CreateSubmitOrderRequest(OrderType orderType, Security security, decimal quantity, string tag, IOrderProperties properties, decimal stopPrice = 0m, decimal limitPrice = 0m, decimal triggerPrice = 0m)

View File

@@ -1981,8 +1981,9 @@ namespace QuantConnect.Algorithm
Universe universe;
if (!UniverseManager.TryGetValue(universeSymbol, out universe))
{
var settings = new UniverseSettings(UniverseSettings) { DataNormalizationMode = DataNormalizationMode.Raw, Resolution = underlyingConfigs.GetHighestResolution() };
universe = _pendingUniverseAdditions.FirstOrDefault(u => u.Configuration.Symbol == universeSymbol)
?? AddUniverse(new OptionContractUniverse(new SubscriptionDataConfig(configs.First(), symbol: universeSymbol), UniverseSettings));
?? AddUniverse(new OptionContractUniverse(new SubscriptionDataConfig(configs.First(), symbol: universeSymbol), settings));
}
// update the universe

View File

@@ -65,7 +65,9 @@ namespace QuantConnect.Algorithm.Selection
_symbols.Remove(removedSymbol);
// the option has been removed! This can happen when the user manually removed the option contract we remove the underlying
if (removedSymbol.SecurityType.IsOption())
// but only if there isn't any other option selected using the same underlying!
if (removedSymbol.SecurityType.IsOption()
&& !_symbols.Any(symbol => symbol.SecurityType.IsOption() && symbol.Underlying == removedSymbol.Underlying))
{
Remove(removedSymbol.Underlying);
}

View File

@@ -41,7 +41,6 @@
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NodaTime" Version="3.0.5" />
<PackageReference Include="RestSharp" Version="106.12.0" />
<PackageReference Include="SharpZipLib" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />

View File

@@ -279,12 +279,12 @@ namespace QuantConnect.Brokerages.Backtesting
continue;
}
var fills = new OrderEvent[0];
var fills = Array.Empty<OrderEvent>();
Security security;
if (!Algorithm.Securities.TryGetValue(order.Symbol, out security))
{
Log.Error("BacktestingBrokerage.Scan(): Unable to process order: " + order.Id + ". The security no longer exists.");
Log.Error($"BacktestingBrokerage.Scan(): Unable to process order: {order.Id}. The security no longer exists. UtcTime: {Algorithm.UtcTime}");
// invalidate the order in the algorithm before removing
OnOrderEvent(new OrderEvent(order,
Algorithm.UtcTime,

View File

@@ -149,7 +149,10 @@ namespace QuantConnect.Brokerages
return SubscriptionManager?.GetSubscribedSymbols() ?? Enumerable.Empty<Symbol>();
}
private void ConnectSync()
/// <summary>
/// Start websocket connect
/// </summary>
protected void ConnectSync()
{
var resetEvent = new ManualResetEvent(false);
EventHandler triggerEvent = (o, args) => resetEvent.Set();

View File

@@ -1,226 +0,0 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using QuantConnect.Brokerages.Binance.Messages;
using QuantConnect.Data.Market;
using QuantConnect.Logging;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Securities;
using System;
using System.Linq;
using Newtonsoft.Json.Linq;
using QuantConnect.Data;
namespace QuantConnect.Brokerages.Binance
{
public partial class BinanceBrokerage
{
private IDataAggregator _aggregator;
/// <summary>
/// Locking object for the Ticks list in the data queue handler
/// </summary>
protected readonly object TickLocker = new object();
private void OnUserMessage(WebSocketMessage webSocketMessage)
{
var e = (WebSocketClientWrapper.TextMessage)webSocketMessage.Data;
try
{
if (Log.DebuggingEnabled)
{
Log.Debug($"BinanceBrokerage.OnUserMessage(): {e.Message}");
}
var obj = JObject.Parse(e.Message);
var objError = obj["error"];
if (objError != null)
{
var error = objError.ToObject<ErrorMessage>();
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, error.Code, error.Message));
return;
}
var objData = obj;
var objEventType = objData["e"];
if (objEventType != null)
{
var eventType = objEventType.ToObject<string>();
switch (eventType)
{
case "executionReport":
var upd = objData.ToObject<Execution>();
if (upd.ExecutionType.Equals("TRADE", StringComparison.OrdinalIgnoreCase))
{
OnFillOrder(upd);
}
break;
}
}
}
catch (Exception exception)
{
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, -1, $"Parsing wss message failed. Data: {e.Message} Exception: {exception}"));
throw;
}
}
private void OnDataMessage(WebSocketMessage webSocketMessage)
{
var e = (WebSocketClientWrapper.TextMessage)webSocketMessage.Data;
try
{
var obj = JObject.Parse(e.Message);
var objError = obj["error"];
if (objError != null)
{
var error = objError.ToObject<ErrorMessage>();
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, error.Code, error.Message));
return;
}
var objData = obj;
var objEventType = objData["e"];
if (objEventType != null)
{
var eventType = objEventType.ToObject<string>();
switch (eventType)
{
case "trade":
var trade = objData.ToObject<Trade>();
EmitTradeTick(
_symbolMapper.GetLeanSymbol(trade.Symbol, SecurityType.Crypto, Market.Binance),
Time.UnixMillisecondTimeStampToDateTime(trade.Time),
trade.Price,
trade.Quantity);
break;
}
}
else if (objData["u"] != null)
{
var quote = objData.ToObject<BestBidAskQuote>();
EmitQuoteTick(
_symbolMapper.GetLeanSymbol(quote.Symbol, SecurityType.Crypto, Market.Binance),
quote.BestBidPrice,
quote.BestBidSize,
quote.BestAskPrice,
quote.BestAskSize);
}
}
catch (Exception exception)
{
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, -1, $"Parsing wss message failed. Data: {e.Message} Exception: {exception}"));
throw;
}
}
private void EmitQuoteTick(Symbol symbol, decimal bidPrice, decimal bidSize, decimal askPrice, decimal askSize)
{
var tick = new Tick
{
AskPrice = askPrice,
BidPrice = bidPrice,
Time = DateTime.UtcNow,
Symbol = symbol,
TickType = TickType.Quote,
AskSize = askSize,
BidSize = bidSize
};
tick.SetValue();
lock (TickLocker)
{
_aggregator.Update(tick);
}
}
private void EmitTradeTick(Symbol symbol, DateTime time, decimal price, decimal quantity)
{
var tick = new Tick
{
Symbol = symbol,
Value = price,
Quantity = Math.Abs(quantity),
Time = time,
TickType = TickType.Trade
};
lock (TickLocker)
{
_aggregator.Update(tick);
}
}
private void OnFillOrder(Execution data)
{
try
{
var order = FindOrderByExternalId(data.OrderId);
if (order == null)
{
// not our order, nothing else to do here
return;
}
var fillPrice = data.LastExecutedPrice;
var fillQuantity = data.Direction == OrderDirection.Sell ? -data.LastExecutedQuantity : data.LastExecutedQuantity;
var updTime = Time.UnixMillisecondTimeStampToDateTime(data.TransactionTime);
var orderFee = new OrderFee(new CashAmount(data.Fee, data.FeeCurrency));
var status = ConvertOrderStatus(data.OrderStatus);
var orderEvent = new OrderEvent
(
order.Id, order.Symbol, updTime, status,
data.Direction, fillPrice, fillQuantity,
orderFee, $"Binance Order Event {data.Direction}"
);
if (status == OrderStatus.Filled)
{
Orders.Order outOrder;
CachedOrderIDs.TryRemove(order.Id, out outOrder);
}
OnOrderEvent(orderEvent);
}
catch (Exception e)
{
Log.Error(e);
throw;
}
}
private Orders.Order FindOrderByExternalId(string brokerId)
{
var order = CachedOrderIDs
.FirstOrDefault(o => o.Value.BrokerId.Contains(brokerId))
.Value;
if (order == null)
{
order = _algorithm.Transactions.GetOrderByBrokerageId(brokerId);
}
return order;
}
}
}

View File

@@ -1,53 +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.Orders;
namespace QuantConnect.Brokerages.Binance
{
/// <summary>
/// Binance utility methods
/// </summary>
public partial class BinanceBrokerage
{
private static OrderStatus ConvertOrderStatus(string raw)
{
switch (raw.LazyToUpper())
{
case "NEW":
return OrderStatus.New;
case "PARTIALLY_FILLED":
return OrderStatus.PartiallyFilled;
case "FILLED":
return OrderStatus.Filled;
case "PENDING_CANCEL":
return OrderStatus.CancelPending;
case "CANCELED":
return OrderStatus.Canceled;
case "REJECTED":
case "EXPIRED":
return OrderStatus.Invalid;
default:
return OrderStatus.None;
}
}
}
}

View File

@@ -1,596 +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 Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using QuantConnect.Configuration;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
using QuantConnect.Logging;
using QuantConnect.Orders;
using QuantConnect.Packets;
using QuantConnect.Securities;
using QuantConnect.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Timer = System.Timers.Timer;
namespace QuantConnect.Brokerages.Binance
{
/// <summary>
/// Binance brokerage implementation
/// </summary>
[BrokerageFactory(typeof(BinanceBrokerageFactory))]
public partial class BinanceBrokerage : BaseWebsocketsBrokerage, IDataQueueHandler
{
private IAlgorithm _algorithm;
private readonly SymbolPropertiesDatabaseSymbolMapper _symbolMapper = new SymbolPropertiesDatabaseSymbolMapper(Market.Binance);
// Binance allows 5 messages per second, but we still get rate limited if we send a lot of messages at that rate
// By sending 3 messages per second, evenly spaced out, we can keep sending messages without being limited
private readonly RateGate _webSocketRateLimiter = new RateGate(1, TimeSpan.FromMilliseconds(330));
private long _lastRequestId;
private LiveNodePacket _job;
private string _webSocketBaseUrl;
private Timer _keepAliveTimer;
private Timer _reconnectTimer;
private BinanceRestApiClient _apiClient;
private BrokerageConcurrentMessageHandler<WebSocketMessage> _messageHandler;
private const int MaximumSymbolsPerConnection = 512;
/// <summary>
/// Constructor for brokerage
/// </summary>
public BinanceBrokerage() : base("Binance")
{
}
/// <summary>
/// Constructor for brokerage
/// </summary>
/// <param name="apiKey">api key</param>
/// <param name="apiSecret">api secret</param>
/// <param name="restApiUrl">The rest api url</param>
/// <param name="webSocketBaseUrl">The web socket base url</param>
/// <param name="algorithm">the algorithm instance is required to retrieve account type</param>
/// <param name="aggregator">the aggregator for consolidating ticks</param>
/// <param name="job">The live job packet</param>
public BinanceBrokerage(string apiKey, string apiSecret, string restApiUrl, string webSocketBaseUrl, IAlgorithm algorithm, IDataAggregator aggregator, LiveNodePacket job)
: base("Binance")
{
Initialize(
wssUrl: webSocketBaseUrl,
restApiUrl: restApiUrl,
apiKey: apiKey,
apiSecret: apiSecret,
algorithm: algorithm,
aggregator: aggregator,
job: job
);
}
#region IBrokerage
/// <summary>
/// Checks if the websocket connection is connected or in the process of connecting
/// </summary>
public override bool IsConnected => WebSocket.IsOpen;
/// <summary>
/// Creates wss connection
/// </summary>
public override void Connect()
{
if (IsConnected)
return;
_apiClient.CreateListenKey();
_reconnectTimer.Start();
WebSocket.Initialize($"{_webSocketBaseUrl}/{_apiClient.SessionId}");
base.Connect();
}
/// <summary>
/// Closes the websockets connection
/// </summary>
public override void Disconnect()
{
_reconnectTimer.Stop();
WebSocket?.Close();
_apiClient.StopSession();
}
/// <summary>
/// Gets all open positions
/// </summary>
/// <returns></returns>
public override List<Holding> GetAccountHoldings()
{
if (_algorithm.BrokerageModel.AccountType == AccountType.Cash)
{
return base.GetAccountHoldings(_job?.BrokerageData, _algorithm.Securities.Values);
}
return _apiClient.GetAccountHoldings();
}
/// <summary>
/// Gets the total account cash balance for specified account type
/// </summary>
/// <returns></returns>
public override List<CashAmount> GetCashBalance()
{
var account = _apiClient.GetCashBalance();
var balances = account.Balances?.Where(balance => balance.Amount > 0).ToList();
if (balances == null || !balances.Any())
return new List<CashAmount>();
return balances
.Select(b => new CashAmount(b.Amount, b.Asset.LazyToUpper()))
.ToList();
}
/// <summary>
/// Gets all orders not yet closed
/// </summary>
/// <returns></returns>
public override List<Order> GetOpenOrders()
{
var orders = _apiClient.GetOpenOrders();
List<Order> list = new List<Order>();
foreach (var item in orders)
{
Order order;
switch (item.Type.LazyToUpper())
{
case "MARKET":
order = new MarketOrder { Price = item.Price };
break;
case "LIMIT":
case "LIMIT_MAKER":
order = new LimitOrder { LimitPrice = item.Price };
break;
case "STOP_LOSS":
case "TAKE_PROFIT":
order = new StopMarketOrder { StopPrice = item.StopPrice, Price = item.Price };
break;
case "STOP_LOSS_LIMIT":
case "TAKE_PROFIT_LIMIT":
order = new StopLimitOrder { StopPrice = item.StopPrice, LimitPrice = item.Price };
break;
default:
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, -1,
"BinanceBrokerage.GetOpenOrders: Unsupported order type returned from brokerage: " + item.Type));
continue;
}
order.Quantity = item.Quantity;
order.BrokerId = new List<string> { item.Id };
order.Symbol = _symbolMapper.GetLeanSymbol(item.Symbol, SecurityType.Crypto, Market.Binance);
order.Time = Time.UnixMillisecondTimeStampToDateTime(item.Time);
order.Status = ConvertOrderStatus(item.Status);
order.Price = item.Price;
if (order.Status.IsOpen())
{
var cached = CachedOrderIDs.Where(c => c.Value.BrokerId.Contains(order.BrokerId.First())).ToList();
if (cached.Any())
{
CachedOrderIDs[cached.First().Key] = order;
}
}
list.Add(order);
}
return list;
}
/// <summary>
/// Places a new order and assigns a new broker ID to the order
/// </summary>
/// <param name="order">The order to be placed</param>
/// <returns>True if the request for a new order has been placed, false otherwise</returns>
public override bool PlaceOrder(Order order)
{
var submitted = false;
_messageHandler.WithLockedStream(() =>
{
submitted = _apiClient.PlaceOrder(order);
});
return submitted;
}
/// <summary>
/// Updates the order with the same id
/// </summary>
/// <param name="order">The new order information</param>
/// <returns>True if the request was made for the order to be updated, false otherwise</returns>
public override bool UpdateOrder(Order order)
{
throw new NotSupportedException("BinanceBrokerage.UpdateOrder: Order update not supported. Please cancel and re-create.");
}
/// <summary>
/// Cancels the order with the specified ID
/// </summary>
/// <param name="order">The order to cancel</param>
/// <returns>True if the request was submitted for cancellation, false otherwise</returns>
public override bool CancelOrder(Order order)
{
var submitted = false;
_messageHandler.WithLockedStream(() =>
{
submitted = _apiClient.CancelOrder(order);
});
return submitted;
}
/// <summary>
/// Gets the history for the requested security
/// </summary>
/// <param name="request">The historical data request</param>
/// <returns>An enumerable of bars covering the span specified in the request</returns>
public override IEnumerable<BaseData> GetHistory(Data.HistoryRequest request)
{
if (request.Resolution == Resolution.Tick || request.Resolution == Resolution.Second)
{
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidResolution",
$"{request.Resolution} resolution is not supported, no history returned"));
yield break;
}
if (request.TickType != TickType.Trade)
{
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidTickType",
$"{request.TickType} tick type not supported, no history returned"));
yield break;
}
var period = request.Resolution.ToTimeSpan();
foreach (var kline in _apiClient.GetHistory(request))
{
yield return new TradeBar()
{
Time = Time.UnixMillisecondTimeStampToDateTime(kline.OpenTime),
Symbol = request.Symbol,
Low = kline.Low,
High = kline.High,
Open = kline.Open,
Close = kline.Close,
Volume = kline.Volume,
Value = kline.Close,
DataType = MarketDataType.TradeBar,
Period = period
};
}
}
/// <summary>
/// Wss message handler
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected override void OnMessage(object sender, WebSocketMessage e)
{
_messageHandler.HandleNewMessage(e);
}
#endregion IBrokerage
#region IDataQueueHandler
/// <summary>
/// Sets the job we're subscribing for
/// </summary>
/// <param name="job">Job we're subscribing for</param>
public void SetJob(LiveNodePacket job)
{
var webSocketBaseUrl = job.BrokerageData["binance-websocket-url"];
var restApiUrl = job.BrokerageData["binance-api-url"];
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"), forceTypeNameOnExisting: false);
Initialize(
wssUrl: webSocketBaseUrl,
restApiUrl: restApiUrl,
apiKey: apiKey,
apiSecret: apiSecret,
algorithm: null,
aggregator: aggregator,
job: job
);
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)
{
if (!CanSubscribe(dataConfig.Symbol))
{
return null;
}
var enumerator = _aggregator.Add(dataConfig, newDataAvailableHandler);
SubscriptionManager.Subscribe(dataConfig);
return enumerator;
}
/// <summary>
/// Removes the specified configuration
/// </summary>
/// <param name="dataConfig">Subscription config to be removed</param>
public void Unsubscribe(SubscriptionDataConfig dataConfig)
{
SubscriptionManager.Unsubscribe(dataConfig);
_aggregator.Remove(dataConfig);
}
/// <summary>
/// Checks if this brokerage supports the specified symbol
/// </summary>
/// <param name="symbol">The symbol</param>
/// <returns>returns true if brokerage supports the specified symbol; otherwise false</returns>
private static bool CanSubscribe(Symbol symbol)
{
return !symbol.Value.Contains("UNIVERSE") &&
symbol.SecurityType == SecurityType.Crypto &&
symbol.ID.Market == Market.Binance;
}
#endregion IDataQueueHandler
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public override void Dispose()
{
_keepAliveTimer.DisposeSafely();
_reconnectTimer.DisposeSafely();
_apiClient.DisposeSafely();
_webSocketRateLimiter.DisposeSafely();
}
/// <summary>
/// Not used
/// </summary>
protected override bool Subscribe(IEnumerable<Symbol> symbols)
{
// NOP
return true;
}
/// <summary>
/// Initialize the instance of this class
/// </summary>
/// <param name="wssUrl">The web socket base url</param>
/// <param name="restApiUrl">The rest api url</param>
/// <param name="apiKey">api key</param>
/// <param name="apiSecret">api secret</param>
/// <param name="algorithm">the algorithm instance is required to retrieve account type</param>
/// <param name="aggregator">the aggregator for consolidating ticks</param>
/// <param name="job">The live job packet</param>
private void Initialize(string wssUrl, string restApiUrl,string apiKey, string apiSecret,
IAlgorithm algorithm, IDataAggregator aggregator, LiveNodePacket job)
{
if (IsInitialized)
{
return;
}
base.Initialize(wssUrl, new WebSocketClientWrapper(), null, apiKey, apiSecret);
_job = job;
_algorithm = algorithm;
_aggregator = aggregator;
_webSocketBaseUrl = wssUrl;
_messageHandler = new BrokerageConcurrentMessageHandler<WebSocketMessage>(OnUserMessage);
var maximumWebSocketConnections = Config.GetInt("binance-maximum-websocket-connections");
var symbolWeights = maximumWebSocketConnections > 0 ? FetchSymbolWeights() : null;
var subscriptionManager = new BrokerageMultiWebSocketSubscriptionManager(
wssUrl,
MaximumSymbolsPerConnection,
maximumWebSocketConnections,
symbolWeights,
() => new BinanceWebSocketWrapper(null),
Subscribe,
Unsubscribe,
OnDataMessage,
new TimeSpan(23, 45, 0));
SubscriptionManager = subscriptionManager;
_apiClient = new BinanceRestApiClient(_symbolMapper,
algorithm?.Portfolio,
apiKey,
apiSecret,
restApiUrl);
_apiClient.OrderSubmit += (s, e) => OnOrderSubmit(e);
_apiClient.OrderStatusChanged += (s, e) => OnOrderEvent(e);
_apiClient.Message += (s, e) => OnMessage(e);
// User data streams will close after 60 minutes. It's recommended to send a ping about every 30 minutes.
// Source: https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#pingkeep-alive-a-listenkey
_keepAliveTimer = new Timer
{
// 30 minutes
Interval = 30 * 60 * 1000
};
_keepAliveTimer.Elapsed += (s, e) => _apiClient.SessionKeepAlive();
WebSocket.Open += (s, e) => { _keepAliveTimer.Start(); };
WebSocket.Closed += (s, e) => { _keepAliveTimer.Stop(); };
// A single connection to stream.binance.com is only valid for 24 hours; expect to be disconnected at the 24 hour mark
// Source: https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#general-wss-information
_reconnectTimer = new Timer
{
// 23.5 hours
Interval = 23.5 * 60 * 60 * 1000
};
_reconnectTimer.Elapsed += (s, e) =>
{
Log.Trace("Daily websocket restart: disconnect");
Disconnect();
Log.Trace("Daily websocket restart: connect");
Connect();
};
}
/// <summary>
/// Subscribes to the requested symbol (using an individual streaming channel)
/// </summary>
/// <param name="webSocket">The websocket instance</param>
/// <param name="symbol">The symbol to subscribe</param>
private bool Subscribe(IWebSocket webSocket, Symbol symbol)
{
Send(webSocket,
new
{
method = "SUBSCRIBE",
@params = new[]
{
$"{symbol.Value.ToLowerInvariant()}@trade",
$"{symbol.Value.ToLowerInvariant()}@bookTicker"
},
id = GetNextRequestId()
}
);
return true;
}
/// <summary>
/// Ends current subscription
/// </summary>
/// <param name="webSocket">The websocket instance</param>
/// <param name="symbol">The symbol to unsubscribe</param>
private bool Unsubscribe(IWebSocket webSocket, Symbol symbol)
{
Send(webSocket,
new
{
method = "UNSUBSCRIBE",
@params = new[]
{
$"{symbol.Value.ToLowerInvariant()}@trade",
$"{symbol.Value.ToLowerInvariant()}@bookTicker"
},
id = GetNextRequestId()
}
);
return true;
}
private void Send(IWebSocket webSocket, object obj)
{
var json = JsonConvert.SerializeObject(obj);
_webSocketRateLimiter.WaitToProceed();
Log.Trace("Send: " + json);
webSocket.Send(json);
}
private long GetNextRequestId()
{
return Interlocked.Increment(ref _lastRequestId);
}
/// <summary>
/// Event invocator for the OrderFilled event
/// </summary>
/// <param name="e">The OrderEvent</param>
private void OnOrderSubmit(BinanceOrderSubmitEventArgs e)
{
var brokerId = e.BrokerId;
var order = e.Order;
if (CachedOrderIDs.ContainsKey(order.Id))
{
CachedOrderIDs[order.Id].BrokerId.Clear();
CachedOrderIDs[order.Id].BrokerId.Add(brokerId);
}
else
{
order.BrokerId.Add(brokerId);
CachedOrderIDs.TryAdd(order.Id, order);
}
}
/// <summary>
/// Returns the weights for each symbol (the weight value is the count of trades in the last 24 hours)
/// </summary>
private static Dictionary<Symbol, int> FetchSymbolWeights()
{
var dict = new Dictionary<Symbol, int>();
try
{
const string url = "https://api.binance.com/api/v3/ticker/24hr";
var json = url.DownloadData();
foreach (var row in JArray.Parse(json))
{
var ticker = row["symbol"].ToObject<string>();
var count = row["count"].ToObject<int>();
var symbol = Symbol.Create(ticker, SecurityType.Crypto, Market.Binance);
dict.Add(symbol, count);
}
}
catch (Exception exception)
{
Log.Error(exception);
throw;
}
return dict;
}
}
}

View File

@@ -1,98 +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.Binance
{
/// <summary>
/// Factory method to create binance Websockets brokerage
/// </summary>
public class BinanceBrokerageFactory : BrokerageFactory
{
/// <summary>
/// Factory constructor
/// </summary>
public BinanceBrokerageFactory() : base(typeof(BinanceBrokerage))
{
}
/// <summary>
/// Not required
/// </summary>
public override void Dispose()
{
}
/// <summary>
/// provides brokerage connection data
/// </summary>
public override Dictionary<string, string> BrokerageData => new Dictionary<string, string>
{
{ "binance-api-key", Config.Get("binance-api-key")},
{ "binance-api-secret", Config.Get("binance-api-secret")},
// paper trading available using https://testnet.binance.vision
{ "binance-api-url", Config.Get("binance-api-url", "https://api.binance.com")},
// paper trading available using wss://testnet.binance.vision/ws
{ "binance-websocket-url", Config.Get("binance-websocket-url", "wss://stream.binance.com:9443/ws")},
// load holdings if available
{ "live-holdings", Config.Get("live-holdings")},
};
/// <summary>
/// The brokerage model
/// </summary>
/// <param name="orderProvider">The order provider</param>
public override IBrokerageModel GetBrokerageModel(IOrderProvider orderProvider) => new BinanceBrokerageModel();
/// <summary>
/// Create the Brokerage instance
/// </summary>
/// <param name="job"></param>
/// <param name="algorithm"></param>
/// <returns></returns>
public override IBrokerage CreateBrokerage(Packets.LiveNodePacket job, IAlgorithm algorithm)
{
var required = new[] { "binance-api-secret", "binance-api-key", "binance-api-url", "binance-websocket-url" };
foreach (var item in required)
{
if (string.IsNullOrEmpty(job.BrokerageData[item]))
{
throw new ArgumentException($"BinanceBrokerageFactory.CreateBrokerage: Missing {item} in config.json");
}
}
var brokerage = new BinanceBrokerage(
job.BrokerageData["binance-api-key"],
job.BrokerageData["binance-api-secret"],
job.BrokerageData["binance-api-url"],
job.BrokerageData["binance-websocket-url"],
algorithm,
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false),
job);
Composer.Instance.AddPart<IDataQueueHandler>(brokerage);
return brokerage;
}
}
}

View File

@@ -1,46 +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.Orders;
namespace QuantConnect.Brokerages.Binance
{
/// <summary>
/// Represents a binance submit order event data
/// </summary>
public class BinanceOrderSubmitEventArgs
{
/// <summary>
/// Order Event Constructor.
/// </summary>
/// <param name="brokerId">Binance order id returned from brokerage</param>
/// <param name="order">Order for this order placement</param>
public BinanceOrderSubmitEventArgs(string brokerId, Order order)
{
BrokerId = brokerId;
Order = order;
}
/// <summary>
/// Original brokerage id
/// </summary>
public string BrokerId { get; set; }
/// <summary>
/// The lean order
/// </summary>
public Order Order { get; set; }
}
}

View File

@@ -1,606 +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 Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using QuantConnect.Logging;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Securities;
using QuantConnect.Util;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
namespace QuantConnect.Brokerages.Binance
{
/// <summary>
/// Binance REST API implementation
/// </summary>
public class BinanceRestApiClient : IDisposable
{
private const string UserDataStreamEndpoint = "/api/v3/userDataStream";
private readonly SymbolPropertiesDatabaseSymbolMapper _symbolMapper;
private readonly ISecurityProvider _securityProvider;
private readonly IRestClient _restClient;
private readonly RateGate _restRateLimiter = new RateGate(10, TimeSpan.FromSeconds(1));
private readonly object _listenKeyLocker = new object();
/// <summary>
/// Event that fires each time an order is filled
/// </summary>
public event EventHandler<BinanceOrderSubmitEventArgs> OrderSubmit;
/// <summary>
/// Event that fires each time an order is filled
/// </summary>
public event EventHandler<OrderEvent> OrderStatusChanged;
/// <summary>
/// Event that fires when an error is encountered in the brokerage
/// </summary>
public event EventHandler<BrokerageMessageEvent> Message;
/// <summary>
/// Key Header
/// </summary>
public readonly string KeyHeader = "X-MBX-APIKEY";
/// <summary>
/// The api secret
/// </summary>
protected string ApiSecret;
/// <summary>
/// The api key
/// </summary>
protected string ApiKey;
/// <summary>
/// Represents UserData Session listen key
/// </summary>
public string SessionId { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="BinanceRestApiClient"/> class.
/// </summary>
/// <param name="symbolMapper">The symbol mapper.</param>
/// <param name="securityProvider">The holdings provider.</param>
/// <param name="apiKey">The Binance API key</param>
/// <param name="apiSecret">The The Binance API secret</param>
/// <param name="restApiUrl">The Binance API rest url</param>
public BinanceRestApiClient(SymbolPropertiesDatabaseSymbolMapper symbolMapper, ISecurityProvider securityProvider,
string apiKey, string apiSecret, string restApiUrl)
{
_symbolMapper = symbolMapper;
_securityProvider = securityProvider;
_restClient = new RestClient(restApiUrl);
ApiKey = apiKey;
ApiSecret = apiSecret;
}
/// <summary>
/// Gets all open positions
/// </summary>
/// <returns></returns>
public List<Holding> GetAccountHoldings()
{
return new List<Holding>();
}
/// <summary>
/// Gets the total account cash balance for specified account type
/// </summary>
/// <returns></returns>
public Messages.AccountInformation GetCashBalance()
{
var queryString = $"timestamp={GetNonce()}";
var endpoint = $"/api/v3/account?{queryString}&signature={AuthenticationToken(queryString)}";
var request = new RestRequest(endpoint, Method.GET);
request.AddHeader(KeyHeader, ApiKey);
var response = ExecuteRestRequest(request);
if (response.StatusCode != HttpStatusCode.OK)
{
throw new Exception($"BinanceBrokerage.GetCashBalance: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
}
return JsonConvert.DeserializeObject<Messages.AccountInformation>(response.Content);
}
/// <summary>
/// Gets all orders not yet closed
/// </summary>
/// <returns></returns>
public IEnumerable<Messages.OpenOrder> GetOpenOrders()
{
var queryString = $"timestamp={GetNonce()}";
var endpoint = $"/api/v3/openOrders?{queryString}&signature={AuthenticationToken(queryString)}";
var request = new RestRequest(endpoint, Method.GET);
request.AddHeader(KeyHeader, ApiKey);
var response = ExecuteRestRequest(request);
if (response.StatusCode != HttpStatusCode.OK)
{
throw new Exception($"BinanceBrokerage.GetCashBalance: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
}
return JsonConvert.DeserializeObject<Messages.OpenOrder[]>(response.Content);
}
/// <summary>
/// Places a new order and assigns a new broker ID to the order
/// </summary>
/// <param name="order">The order to be placed</param>
/// <returns>True if the request for a new order has been placed, false otherwise</returns>
public bool PlaceOrder(Order order)
{
// supported time in force values {GTC, IOC, FOK}
// use GTC as LEAN doesn't support others yet
IDictionary<string, object> body = new Dictionary<string, object>()
{
{ "symbol", _symbolMapper.GetBrokerageSymbol(order.Symbol) },
{ "quantity", Math.Abs(order.Quantity).ToString(CultureInfo.InvariantCulture) },
{ "side", ConvertOrderDirection(order.Direction) }
};
switch (order)
{
case LimitOrder limitOrder:
body["type"] = (order.Properties as BinanceOrderProperties)?.PostOnly == true
? "LIMIT_MAKER"
: "LIMIT";
body["price"] = limitOrder.LimitPrice.ToString(CultureInfo.InvariantCulture);
// timeInForce is not required for LIMIT_MAKER
if (Equals(body["type"], "LIMIT"))
body["timeInForce"] = "GTC";
break;
case MarketOrder:
body["type"] = "MARKET";
break;
case StopLimitOrder stopLimitOrder:
var ticker = GetTickerPrice(order);
var stopPrice = stopLimitOrder.StopPrice;
if (order.Direction == OrderDirection.Sell)
{
body["type"] = stopPrice <= ticker ? "STOP_LOSS_LIMIT" : "TAKE_PROFIT_LIMIT";
}
else
{
body["type"] = stopPrice <= ticker ? "TAKE_PROFIT_LIMIT" : "STOP_LOSS_LIMIT";
}
body["timeInForce"] = "GTC";
body["stopPrice"] = stopPrice.ToStringInvariant();
body["price"] = stopLimitOrder.LimitPrice.ToStringInvariant();
break;
default:
throw new NotSupportedException($"BinanceBrokerage.ConvertOrderType: Unsupported order type: {order.Type}");
}
const string endpoint = "/api/v3/order";
body["timestamp"] = GetNonce();
body["signature"] = AuthenticationToken(body.ToQueryString());
var request = new RestRequest(endpoint, Method.POST);
request.AddHeader(KeyHeader, ApiKey);
request.AddParameter(
"application/x-www-form-urlencoded",
Encoding.UTF8.GetBytes(body.ToQueryString()),
ParameterType.RequestBody
);
var response = ExecuteRestRequest(request);
if (response.StatusCode == HttpStatusCode.OK)
{
var raw = JsonConvert.DeserializeObject<Messages.NewOrder>(response.Content);
if (string.IsNullOrEmpty(raw?.Id))
{
var errorMessage = $"Error parsing response from place order: {response.Content}";
OnOrderEvent(new OrderEvent(
order,
DateTime.UtcNow,
OrderFee.Zero,
"Binance Order Event")
{ Status = OrderStatus.Invalid, Message = errorMessage });
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, (int)response.StatusCode, errorMessage));
return true;
}
OnOrderSubmit(raw, order);
return true;
}
var message = $"Order failed, Order Id: {order.Id} timestamp: {order.Time} quantity: {order.Quantity} content: {response.Content}";
OnOrderEvent(new OrderEvent(
order,
DateTime.UtcNow,
OrderFee.Zero,
"Binance Order Event")
{ Status = OrderStatus.Invalid });
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, -1, message));
return true;
}
/// <summary>
/// Cancels the order with the specified ID
/// </summary>
/// <param name="order">The order to cancel</param>
/// <returns>True if the request was submitted for cancellation, false otherwise</returns>
public bool CancelOrder(Order order)
{
var success = new List<bool>();
IDictionary<string, object> body = new Dictionary<string, object>()
{
{ "symbol", _symbolMapper.GetBrokerageSymbol(order.Symbol) }
};
foreach (var id in order.BrokerId)
{
if (body.ContainsKey("signature"))
{
body.Remove("signature");
}
body["orderId"] = id;
body["timestamp"] = GetNonce();
body["signature"] = AuthenticationToken(body.ToQueryString());
var request = new RestRequest("/api/v3/order", Method.DELETE);
request.AddHeader(KeyHeader, ApiKey);
request.AddParameter(
"application/x-www-form-urlencoded",
Encoding.UTF8.GetBytes(body.ToQueryString()),
ParameterType.RequestBody
);
var response = ExecuteRestRequest(request);
success.Add(response.StatusCode == HttpStatusCode.OK);
}
var canceled = false;
if (success.All(a => a))
{
OnOrderEvent(new OrderEvent(order,
DateTime.UtcNow,
OrderFee.Zero,
"Binance Order Event")
{ Status = OrderStatus.Canceled });
canceled = true;
}
return canceled;
}
/// <summary>
/// Gets the history for the requested security
/// </summary>
/// <param name="request">The historical data request</param>
/// <returns>An enumerable of bars covering the span specified in the request</returns>
public IEnumerable<Messages.Kline> GetHistory(Data.HistoryRequest request)
{
var resolution = ConvertResolution(request.Resolution);
var resolutionInMs = (long)request.Resolution.ToTimeSpan().TotalMilliseconds;
var symbol = _symbolMapper.GetBrokerageSymbol(request.Symbol);
var startMs = (long)Time.DateTimeToUnixTimeStamp(request.StartTimeUtc) * 1000;
var endMs = (long)Time.DateTimeToUnixTimeStamp(request.EndTimeUtc) * 1000;
// we always use the real endpoint for history requests
var endpoint = $"https://api.binance.com/api/v3/klines?symbol={symbol}&interval={resolution}&limit=1000";
while (endMs - startMs >= resolutionInMs)
{
var timeframe = $"&startTime={startMs}&endTime={endMs}";
var restRequest = new RestRequest(endpoint + timeframe, Method.GET);
var response = ExecuteRestRequest(restRequest);
if (response.StatusCode != HttpStatusCode.OK)
{
throw new Exception($"BinanceBrokerage.GetHistory: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
}
var klines = JsonConvert.DeserializeObject<object[][]>(response.Content)
.Select(entries => new Messages.Kline(entries))
.ToList();
if (klines.Count > 0)
{
var lastValue = klines[klines.Count - 1];
if (Log.DebuggingEnabled)
{
var windowStartTime = Time.UnixMillisecondTimeStampToDateTime(klines[0].OpenTime);
var windowEndTime = Time.UnixMillisecondTimeStampToDateTime(lastValue.OpenTime + resolutionInMs);
Log.Debug($"BinanceRestApiClient.GetHistory(): Received [{symbol}] data for timeperiod from {windowStartTime.ToStringInvariant()} to {windowEndTime.ToStringInvariant()}..");
}
startMs = lastValue.OpenTime + resolutionInMs;
foreach (var kline in klines)
{
yield return kline;
}
}
else
{
// if there is no data just break
break;
}
}
}
/// <summary>
/// Check User Data stream listen key is alive
/// </summary>
/// <returns></returns>
public bool SessionKeepAlive()
{
if (string.IsNullOrEmpty(SessionId))
{
throw new Exception("BinanceBrokerage:UserStream. listenKey wasn't allocated or has been refused.");
}
var ping = new RestRequest(UserDataStreamEndpoint, Method.PUT);
ping.AddHeader(KeyHeader, ApiKey);
ping.AddParameter(
"application/x-www-form-urlencoded",
Encoding.UTF8.GetBytes($"listenKey={SessionId}"),
ParameterType.RequestBody
);
var pong = ExecuteRestRequest(ping);
return pong.StatusCode == HttpStatusCode.OK;
}
/// <summary>
/// Stops the session
/// </summary>
public void StopSession()
{
if (!string.IsNullOrEmpty(SessionId))
{
var request = new RestRequest(UserDataStreamEndpoint, Method.DELETE);
request.AddHeader(KeyHeader, ApiKey);
request.AddParameter(
"application/x-www-form-urlencoded",
Encoding.UTF8.GetBytes($"listenKey={SessionId}"),
ParameterType.RequestBody
);
ExecuteRestRequest(request);
}
}
/// <summary>
/// Provides the current tickers price
/// </summary>
/// <returns></returns>
public Messages.PriceTicker[] GetTickers()
{
const string endpoint = "/api/v3/ticker/price";
var req = new RestRequest(endpoint, Method.GET);
var response = ExecuteRestRequest(req);
if (response.StatusCode != HttpStatusCode.OK)
{
throw new Exception($"BinanceBrokerage.GetTick: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
}
return JsonConvert.DeserializeObject<Messages.PriceTicker[]>(response.Content);
}
/// <summary>
/// Start user data stream
/// </summary>
public void CreateListenKey()
{
var request = new RestRequest(UserDataStreamEndpoint, Method.POST);
request.AddHeader(KeyHeader, ApiKey);
var response = ExecuteRestRequest(request);
if (response.StatusCode != HttpStatusCode.OK)
{
throw new Exception($"BinanceBrokerage.StartSession: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
}
var content = JObject.Parse(response.Content);
lock (_listenKeyLocker)
{
SessionId = content.Value<string>("listenKey");
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
_restRateLimiter.DisposeSafely();
}
/// <summary>
/// If an IP address exceeds a certain number of requests per minute
/// HTTP 429 return code is used when breaking a request rate limit.
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private IRestResponse ExecuteRestRequest(IRestRequest request)
{
const int maxAttempts = 10;
var attempts = 0;
IRestResponse response;
do
{
if (!_restRateLimiter.WaitToProceed(TimeSpan.Zero))
{
Log.Trace("Brokerage.OnMessage(): " + new BrokerageMessageEvent(BrokerageMessageType.Warning, "RateLimit",
"The API request has been rate limited. To avoid this message, please reduce the frequency of API calls."));
_restRateLimiter.WaitToProceed();
}
response = _restClient.Execute(request);
// 429 status code: Too Many Requests
} while (++attempts < maxAttempts && (int)response.StatusCode == 429);
return response;
}
private decimal GetTickerPrice(Order order)
{
var security = _securityProvider.GetSecurity(order.Symbol);
var tickerPrice = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
if (tickerPrice == 0)
{
var brokerageSymbol = _symbolMapper.GetBrokerageSymbol(order.Symbol);
var tickers = GetTickers();
var ticker = tickers.FirstOrDefault(t => t.Symbol == brokerageSymbol);
if (ticker == null)
{
throw new KeyNotFoundException($"BinanceBrokerage: Unable to resolve currency conversion pair: {order.Symbol}");
}
tickerPrice = ticker.Price;
}
return tickerPrice;
}
/// <summary>
/// Timestamp in milliseconds
/// </summary>
/// <returns></returns>
private long GetNonce()
{
return (long)(Time.TimeStamp() * 1000);
}
/// <summary>
/// Creates a signature for signed endpoints
/// </summary>
/// <param name="payload">the body of the request</param>
/// <returns>a token representing the request params</returns>
private string AuthenticationToken(string payload)
{
using (HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(ApiSecret)))
{
return hmac.ComputeHash(Encoding.UTF8.GetBytes(payload)).ToHexString();
}
}
private static string ConvertOrderDirection(OrderDirection orderDirection)
{
if (orderDirection == OrderDirection.Buy || orderDirection == OrderDirection.Sell)
{
return orderDirection.ToString().LazyToUpper();
}
throw new NotSupportedException($"BinanceBrokerage.ConvertOrderDirection: Unsupported order direction: {orderDirection}");
}
private readonly Dictionary<Resolution, string> _knownResolutions = new Dictionary<Resolution, string>()
{
{ Resolution.Minute, "1m" },
{ Resolution.Hour, "1h" },
{ Resolution.Daily, "1d" }
};
private string ConvertResolution(Resolution resolution)
{
if (_knownResolutions.ContainsKey(resolution))
{
return _knownResolutions[resolution];
}
else
{
throw new ArgumentException($"BinanceBrokerage.ConvertResolution: Unsupported resolution type: {resolution}");
}
}
/// <summary>
/// Event invocator for the OrderFilled event
/// </summary>
/// <param name="newOrder">The brokerage order submit result</param>
/// <param name="order">The lean order</param>
private void OnOrderSubmit(Messages.NewOrder newOrder, Order order)
{
try
{
OrderSubmit?.Invoke(
this,
new BinanceOrderSubmitEventArgs(newOrder.Id, order));
// Generate submitted event
OnOrderEvent(new OrderEvent(
order,
Time.UnixMillisecondTimeStampToDateTime(newOrder.TransactionTime),
OrderFee.Zero,
"Binance Order Event")
{ Status = OrderStatus.Submitted }
);
Log.Trace($"Order submitted successfully - OrderId: {order.Id}");
}
catch (Exception err)
{
Log.Error(err);
}
}
/// <summary>
/// Event invocator for the OrderFilled event
/// </summary>
/// <param name="e">The OrderEvent</param>
private void OnOrderEvent(OrderEvent e)
{
try
{
Log.Debug("Brokerage.OnOrderEvent(): " + e);
OrderStatusChanged?.Invoke(this, e);
}
catch (Exception err)
{
Log.Error(err);
}
}
/// <summary>
/// Event invocator for the Message event
/// </summary>
/// <param name="e">The error</param>
protected virtual void OnMessage(BrokerageMessageEvent e)
{
try
{
if (e.Type == BrokerageMessageType.Error)
{
Log.Error("Brokerage.OnMessage(): " + e);
}
else
{
Log.Trace("Brokerage.OnMessage(): " + e);
}
Message?.Invoke(this, e);
}
catch (Exception err)
{
Log.Error(err);
}
}
}
}

View File

@@ -1,44 +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;
namespace QuantConnect.Brokerages.Binance
{
/// <summary>
/// Wrapper class for a Binance websocket connection
/// </summary>
public class BinanceWebSocketWrapper : WebSocketClientWrapper
{
/// <summary>
/// The unique Id for the connection
/// </summary>
public string ConnectionId { get; }
/// <summary>
/// The handler for the connection
/// </summary>
public IConnectionHandler ConnectionHandler { get; }
/// <summary>
/// Initializes a new instance of the <see cref="BinanceWebSocketWrapper"/> class.
/// </summary>
public BinanceWebSocketWrapper(IConnectionHandler connectionHandler)
{
ConnectionId = Guid.NewGuid().ToString();
ConnectionHandler = connectionHandler;
}
}
}

View File

@@ -1,209 +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 Newtonsoft.Json;
using QuantConnect.Orders;
using System;
using System.Globalization;
namespace QuantConnect.Brokerages.Binance.Messages
{
#pragma warning disable 1591
public class AccountInformation
{
public Balance[] Balances { get; set; }
public class Balance
{
public string Asset { get; set; }
public decimal Free { get; set; }
public decimal Locked { get; set; }
public decimal Amount => Free + Locked;
}
}
public class PriceTicker
{
public string Symbol { get; set; }
public decimal Price { get; set; }
}
public class Order
{
[JsonProperty("orderId")]
public string Id { get; set; }
public string Symbol { get; set; }
public decimal Price { get; set; }
public decimal StopPrice { get; set; }
[JsonProperty("origQty")]
public decimal OriginalAmount { get; set; }
[JsonProperty("executedQty")]
public decimal ExecutedAmount { get; set; }
public string Status { get; set; }
public string Type { get; set; }
public string Side { get; set; }
public decimal Quantity => string.Equals(Side, "buy", StringComparison.OrdinalIgnoreCase) ? OriginalAmount : -OriginalAmount;
}
public class OpenOrder : Order
{
public long Time { get; set; }
}
public class NewOrder : Order
{
[JsonProperty("transactTime")]
public long TransactionTime { get; set; }
}
public enum EventType
{
None,
OrderBook,
Trade,
Execution
}
public class ErrorMessage
{
[JsonProperty("code")]
public int Code { get; set; }
[JsonProperty("msg")]
public string Message { get; set; }
}
public class BestBidAskQuote
{
[JsonProperty("u")]
public long OrderBookUpdateId { get; set; }
[JsonProperty("s")]
public string Symbol { get; set; }
[JsonProperty("b")]
public decimal BestBidPrice { get; set; }
[JsonProperty("B")]
public decimal BestBidSize { get; set; }
[JsonProperty("a")]
public decimal BestAskPrice { get; set; }
[JsonProperty("A")]
public decimal BestAskSize { get; set; }
}
public class BaseMessage
{
public virtual EventType @Event { get; } = EventType.None;
[JsonProperty("e")]
public string EventName { get; set; }
[JsonProperty("E")]
public long Time { get; set; }
[JsonProperty("s")]
public string Symbol { get; set; }
}
public class Trade : BaseMessage
{
public override EventType @Event => EventType.Trade;
[JsonProperty("T")]
public new long Time { get; set; }
[JsonProperty("p")]
public decimal Price { get; private set; }
[JsonProperty("q")]
public decimal Quantity { get; private set; }
}
public class Execution : BaseMessage
{
public override EventType @Event => EventType.Execution;
[JsonProperty("i")]
public string OrderId { get; set; }
[JsonProperty("t")]
public string TradeId { get; set; }
[JsonProperty("I")]
public string Ignore { get; set; }
[JsonProperty("x")]
public string ExecutionType { get; private set; }
[JsonProperty("X")]
public string OrderStatus { get; private set; }
[JsonProperty("T")]
public long TransactionTime { get; set; }
[JsonProperty("L")]
public decimal LastExecutedPrice { get; set; }
[JsonProperty("l")]
public decimal LastExecutedQuantity { get; set; }
[JsonProperty("S")]
public string Side { get; set; }
[JsonProperty("n")]
public decimal Fee { get; set; }
[JsonProperty("N")]
public string FeeCurrency { get; set; }
public OrderDirection Direction => Side.Equals("BUY", StringComparison.OrdinalIgnoreCase) ? OrderDirection.Buy : OrderDirection.Sell;
}
public class Kline
{
public long OpenTime { get; }
public decimal Open { get; }
public decimal Close { get; }
public decimal High { get; }
public decimal Low { get; }
public decimal Volume { get; }
public Kline() { }
public Kline(long msts, decimal close)
{
OpenTime = msts;
Open = Close = High = Low = close;
Volume = 0;
}
public Kline(object[] entries)
{
OpenTime = Convert.ToInt64(entries[0], CultureInfo.InvariantCulture);
Open = ((string)entries[1]).ToDecimal();
High = ((string)entries[2]).ToDecimal();
Low = ((string)entries[3]).ToDecimal();
Close = ((string)entries[4]).ToDecimal();
Volume = ((string)entries[5]).ToDecimal();
}
}
#pragma warning restore 1591
}

View File

@@ -24,7 +24,6 @@ using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Packets;
using QuantConnect.Securities;
using QuantConnect.Securities.Crypto;
using QuantConnect.Util;
using RestSharp;
using System;
@@ -49,7 +48,6 @@ namespace QuantConnect.Brokerages.Bitfinex
private IAlgorithm _algorithm;
private readonly RateGate _restRateLimiter = new RateGate(10, TimeSpan.FromMinutes(1));
private readonly ConcurrentDictionary<int, decimal> _fills = new ConcurrentDictionary<int, decimal>();
private SymbolPropertiesDatabase _symbolPropertiesDatabase;
private IDataAggregator _aggregator;
// map Bitfinex ClientOrderId -> LEAN order (only used for orders submitted in PlaceOrder, not for existing orders)
@@ -188,7 +186,6 @@ namespace QuantConnect.Brokerages.Bitfinex
TimeSpan.Zero,
_connectionRateLimiter);
_symbolPropertiesDatabase = SymbolPropertiesDatabase.FromDataFolder();
_algorithm = algorithm;
_aggregator = aggregator;
@@ -473,27 +470,14 @@ namespace QuantConnect.Brokerages.Bitfinex
: OrderStatus.PartiallyFilled;
}
if (_algorithm.BrokerageModel.AccountType == AccountType.Cash &&
order.Direction == OrderDirection.Buy)
if (_algorithm.BrokerageModel.AccountType == AccountType.Cash && order.Direction == OrderDirection.Buy)
{
var symbolProperties = _symbolPropertiesDatabase.GetSymbolProperties(symbol.ID.Market,
symbol,
symbol.SecurityType,
AccountBaseCurrency);
Crypto.DecomposeCurrencyPair(symbol, symbolProperties, out var baseCurrency, out var _);
CurrencyPairUtil.DecomposeCurrencyPair(symbol, out var baseCurrency, out _);
if (orderFee.Value.Currency != baseCurrency)
{
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, "UnexpectedFeeCurrency", $"Unexpected fee currency {orderFee.Value.Currency} for symbol {symbol}. OrderId {order.Id}. BrokerageOrderId {brokerId}. " +
"This error can happen because your account is Margin type and Lean is configured to be Cash type or while using Cash type the Bitfinex account fee settings are set to 'Asset Trading Fee' and should be set to 'Currency Exchange Fee'."));
}
else
{
// fees are debited in the base currency, so we have to subtract them from the filled quantity
fillQuantity -= orderFee.Value.Amount;
orderFee = new ModifiedFillQuantityOrderFee(orderFee.Value);
}
}
var orderEvent = new OrderEvent

View File

@@ -23,7 +23,6 @@ using QuantConnect.Logging;
using QuantConnect.Orders;
using QuantConnect.Packets;
using QuantConnect.Securities;
using QuantConnect.Securities.Crypto;
using QuantConnect.Util;
using RestSharp;
using System;
@@ -308,16 +307,7 @@ namespace QuantConnect.Brokerages.Bitfinex
foreach (var holding in GetAccountHoldings().Where(x => x.Symbol.SecurityType == SecurityType.Crypto))
{
var defaultQuoteCurrency = _algorithm.Portfolio.CashBook.AccountCurrency;
var symbolProperties = _symbolPropertiesDatabase.GetSymbolProperties(
holding.Symbol.ID.Market,
holding.Symbol,
holding.Symbol.SecurityType,
defaultQuoteCurrency);
string baseCurrency;
string quoteCurrency;
Crypto.DecomposeCurrencyPair(holding.Symbol, symbolProperties, out baseCurrency, out quoteCurrency);
CurrencyPairUtil.DecomposeCurrencyPair(holding.Symbol, out var baseCurrency, out var quoteCurrency, defaultQuoteCurrency);
var baseQuantity = holding.Quantity;
CashAmount baseCurrencyAmount;

View File

@@ -1,4 +1,4 @@
/*
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
@@ -105,29 +105,29 @@ namespace QuantConnect.Brokerages.Tradier
var start = request.StartTimeUtc.ConvertTo(DateTimeZone.Utc, TimeZones.NewYork);
var end = request.EndTimeUtc.ConvertTo(DateTimeZone.Utc, TimeZones.NewYork);
var history = Enumerable.Empty<Slice>();
switch (request.Resolution)
{
case Resolution.Tick:
history = GetHistoryTick(request.Symbol, start, end);
history = GetHistoryTick(request, start, end);
break;
case Resolution.Second:
history = GetHistorySecond(request.Symbol, start, end);
history = GetHistorySecond(request, start, end);
break;
case Resolution.Minute:
history = GetHistoryMinute(request.Symbol, start, end);
history = GetHistoryMinute(request, start, end);
break;
case Resolution.Hour:
history = GetHistoryHour(request.Symbol, start, end);
history = GetHistoryHour(request, start, end);
break;
case Resolution.Daily:
history = GetHistoryDaily(request.Symbol, start, end);
history = GetHistoryDaily(request, start, end);
break;
}
@@ -174,8 +174,10 @@ namespace QuantConnect.Brokerages.Tradier
ReaderErrorDetected?.Invoke(this, e);
}
private IEnumerable<Slice> GetHistoryTick(Symbol symbol, DateTime start, DateTime end)
private IEnumerable<Slice> GetHistoryTick(HistoryRequest request, DateTime start, DateTime end)
{
var symbol = request.Symbol;
var exchangeTz = request.ExchangeHours.TimeZone;
var history = GetTimeSeries(symbol, start, end, TradierTimeSeriesIntervals.Tick);
if (history == null)
@@ -192,11 +194,13 @@ namespace QuantConnect.Brokerages.Tradier
TickType = TickType.Trade,
Quantity = Convert.ToInt32(tick.Volume)
})
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }));
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }, tradeBar.EndTime.ConvertToUtc(exchangeTz)));
}
private IEnumerable<Slice> GetHistorySecond(Symbol symbol, DateTime start, DateTime end)
private IEnumerable<Slice> GetHistorySecond(HistoryRequest request, DateTime start, DateTime end)
{
var symbol = request.Symbol;
var exchangeTz = request.ExchangeHours.TimeZone;
var history = GetTimeSeries(symbol, start, end, TradierTimeSeriesIntervals.Tick);
if (history == null)
@@ -222,7 +226,7 @@ namespace QuantConnect.Brokerages.Tradier
g.Last().LastPrice,
g.Sum(t => t.Quantity),
Time.OneSecond))
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }))
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }, tradeBar.EndTime.ConvertToUtc(exchangeTz)))
.ToList();
DataPointCount += result.Count;
@@ -230,8 +234,10 @@ namespace QuantConnect.Brokerages.Tradier
return result;
}
private IEnumerable<Slice> GetHistoryMinute(Symbol symbol, DateTime start, DateTime end)
private IEnumerable<Slice> GetHistoryMinute(HistoryRequest request, DateTime start, DateTime end)
{
var symbol = request.Symbol;
var exchangeTz = request.ExchangeHours.TimeZone;
var history = GetTimeSeries(symbol, start, end, TradierTimeSeriesIntervals.OneMinute);
if (history == null)
@@ -241,11 +247,13 @@ namespace QuantConnect.Brokerages.Tradier
return history
.Select(bar => new TradeBar(bar.Time, symbol, bar.Open, bar.High, bar.Low, bar.Close, bar.Volume, Time.OneMinute))
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }));
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }, tradeBar.EndTime.ConvertToUtc(exchangeTz)));
}
private IEnumerable<Slice> GetHistoryHour(Symbol symbol, DateTime start, DateTime end)
private IEnumerable<Slice> GetHistoryHour(HistoryRequest request, DateTime start, DateTime end)
{
var symbol = request.Symbol;
var exchangeTz = request.ExchangeHours.TimeZone;
var history = GetTimeSeries(symbol, start, end, TradierTimeSeriesIntervals.FifteenMinutes);
if (history == null)
@@ -264,7 +272,7 @@ namespace QuantConnect.Brokerages.Tradier
g.Last().Close,
g.Sum(t => t.Volume),
Time.OneHour))
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }))
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }, tradeBar.EndTime.ConvertToUtc(exchangeTz)))
.ToList();
DataPointCount += result.Count;
@@ -272,15 +280,17 @@ namespace QuantConnect.Brokerages.Tradier
return result;
}
private IEnumerable<Slice> GetHistoryDaily(Symbol symbol, DateTime start, DateTime end)
private IEnumerable<Slice> GetHistoryDaily(HistoryRequest request, DateTime start, DateTime end)
{
var symbol = request.Symbol;
var exchangeTz = request.ExchangeHours.TimeZone;
var history = GetHistoricalData(symbol, start, end);
DataPointCount += history.Count;
return history
.Select(bar => new TradeBar(bar.Time, symbol, bar.Open, bar.High, bar.Low, bar.Close, bar.Volume, Time.OneDay))
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }));
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }, tradeBar.EndTime.ConvertToUtc(exchangeTz)));
}
#endregion

View File

@@ -148,7 +148,7 @@ namespace QuantConnect.Brokerages
/// <summary>
/// Wraps IsAlive
/// </summary>
public bool IsOpen => _client != null && _client.State == WebSocketState.Open;
public bool IsOpen => _client?.State == WebSocketState.Open;
/// <summary>
/// Wraps message event

View File

@@ -61,7 +61,6 @@ from QuantConnect.Data.Shortable import *
from QuantConnect.Orders.Slippage import *
from QuantConnect.Securities.Forex import *
from QuantConnect.Data.Fundamental import *
from QuantConnect.Algorithm.CSharp import *
from QuantConnect.Securities.Option import *
from QuantConnect.Securities.Equity import *
from QuantConnect.Securities.Future import *

View File

@@ -66,13 +66,25 @@ namespace QuantConnect.Api
/// </returns>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
if (reader.TokenType == JsonToken.StartArray)
{
if (JArray.Load(reader).Count == 0)
{
return new ParameterSet(-1, new Dictionary<string, string>());
}
}
else if (reader.TokenType == JsonToken.StartObject)
{
var jObject = JObject.Load(reader);
var value = jObject["parameterSet"] ?? jObject;
var value = jObject["parameterSet"] ?? jObject;
var parameterSet = new ParameterSet(-1, value.ToObject<Dictionary<string, string>>());
var parameterSet = new ParameterSet(-1, value.ToObject<Dictionary<string, string>>());
return parameterSet;
return parameterSet;
}
throw new ArgumentException($"Unexpected Tokentype {reader.TokenType}");
}
}
}

View File

@@ -18,7 +18,6 @@ using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Securities;
using QuantConnect.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using static QuantConnect.StringExtensions;
@@ -30,10 +29,17 @@ namespace QuantConnect.Brokerages
/// </summary>
public class BinanceBrokerageModel : DefaultBrokerageModel
{
private const decimal _defaultLeverage = 3;
/// <summary>
/// Market name
/// </summary>
protected virtual string MarketName => Market.Binance;
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets();
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets(Market.Binance);
/// <summary>
/// Initializes a new instance of the <see cref="BinanceBrokerageModel"/> class
@@ -41,22 +47,6 @@ namespace QuantConnect.Brokerages
/// <param name="accountType">The type of account to be modeled, defaults to <see cref="AccountType.Cash"/></param>
public BinanceBrokerageModel(AccountType accountType = AccountType.Cash) : base(accountType)
{
if (accountType == AccountType.Margin)
{
throw new ArgumentException("The Binance brokerage does not currently support Margin trading.");
}
}
/// <summary>
/// Gets a new buying power model for the security, returning the default model with the security's configured leverage.
/// For cash accounts, leverage = 1 is used.
/// Margin trading is not currently supported
/// </summary>
/// <param name="security">The security to get a buying power model for</param>
/// <returns>The buying power model for this brokerage/security</returns>
public override IBuyingPowerModel GetBuyingPowerModel(Security security)
{
return new CashBuyingPowerModel();
}
/// <summary>
@@ -66,8 +56,12 @@ namespace QuantConnect.Brokerages
/// <returns></returns>
public override decimal GetLeverage(Security security)
{
// margin trading is not currently supported by Binance
return 1m;
if (AccountType == AccountType.Cash || security.IsInternalFeed() || security.Type == SecurityType.Base)
{
return 1m;
}
return _defaultLeverage;
}
/// <summary>
@@ -77,7 +71,7 @@ namespace QuantConnect.Brokerages
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("BTCUSDC", SecurityType.Crypto, Market.Binance);
var symbol = Symbol.Create("BTCUSDC", SecurityType.Crypto, MarketName);
return SecurityBenchmark.CreateInstance(securities, symbol);
}
@@ -187,10 +181,10 @@ namespace QuantConnect.Brokerages
order.AbsoluteQuantity * price > security.SymbolProperties.MinimumOrderSize;
}
private static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets()
protected static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets(string marketName)
{
var map = DefaultMarketMap.ToDictionary();
map[SecurityType.Crypto] = Market.Binance;
map[SecurityType.Crypto] = marketName;
return map.ToReadOnlyDictionary();
}
}

View File

@@ -0,0 +1,60 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using QuantConnect.Securities;
using System;
using System.Collections.Generic;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides Binance.US specific properties
/// </summary>
public class BinanceUSBrokerageModel : BinanceBrokerageModel
{
/// <summary>
/// Market name
/// </summary>
protected override string MarketName => Market.BinanceUS;
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets(Market.BinanceUS);
/// <summary>
/// Initializes a new instance of the <see cref="BinanceBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modeled, defaults to <see cref="AccountType.Cash"/></param>
public BinanceUSBrokerageModel(AccountType accountType = AccountType.Cash) : base(accountType)
{
if (accountType == AccountType.Margin)
{
throw new ArgumentException("The Binance.US brokerage does not currently support Margin trading.");
}
}
/// <summary>
/// Binance global leverage rule
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override decimal GetLeverage(Security security)
{
// margin trading is not currently supported by Binance.US
return 1m;
}
}
}

View File

@@ -45,20 +45,6 @@ namespace QuantConnect.Brokerages
{
}
/// <summary>
/// Gets a new buying power model for the security, returning the default model with the security's configured leverage.
/// For cash accounts, leverage = 1 is used.
/// For margin trading, max leverage = 3.3
/// </summary>
/// <param name="security">The security to get a buying power model for</param>
/// <returns>The buying power model for this brokerage/security</returns>
public override IBuyingPowerModel GetBuyingPowerModel(Security security)
{
return AccountType == AccountType.Cash
? (IBuyingPowerModel)new CashBuyingPowerModel()
: new SecurityMarginModel(_maxLeverage);
}
/// <summary>
/// Bitfinex global leverage rule
/// </summary>

View File

@@ -95,7 +95,7 @@ namespace QuantConnect.Brokerages
/// Transaction and submit/execution rules will use TradingTechnologies models
/// </summary>
TradingTechnologies,
/// <summary>
/// Transaction and submit/execution rules will use Kraken models
/// </summary>
@@ -109,6 +109,16 @@ namespace QuantConnect.Brokerages
/// <summary>
/// Transaction and submit/execution rules will use ftx us models
/// </summary>
FTXUS
FTXUS,
/// <summary>
/// Transaction and submit/execution rules will use Exante models
/// </summary>
Exante,
/// <summary>
/// Transaction and submit/execution rules will use Binance.US models
/// </summary>
BinanceUS,
}
}

View File

@@ -351,33 +351,16 @@ namespace QuantConnect.Brokerages
/// <returns>The buying power model for this brokerage/security</returns>
public virtual IBuyingPowerModel GetBuyingPowerModel(Security security)
{
var leverage = GetLeverage(security);
IBuyingPowerModel model;
switch (security.Type)
return security.Type switch
{
case SecurityType.Crypto:
model = new CashBuyingPowerModel();
break;
case SecurityType.Forex:
case SecurityType.Cfd:
model = new SecurityMarginModel(leverage, RequiredFreeBuyingPowerPercent);
break;
case SecurityType.Option:
model = new OptionMarginModel(RequiredFreeBuyingPowerPercent);
break;
case SecurityType.FutureOption:
model = new FuturesOptionsMarginModel(RequiredFreeBuyingPowerPercent, (Option)security);
break;
case SecurityType.Future:
model = new FutureMarginModel(RequiredFreeBuyingPowerPercent, security);
break;
case SecurityType.Index:
default:
model = new SecurityMarginModel(leverage, RequiredFreeBuyingPowerPercent);
break;
}
return model;
SecurityType.Future => new FutureMarginModel(RequiredFreeBuyingPowerPercent, security),
SecurityType.FutureOption => new FuturesOptionsMarginModel(RequiredFreeBuyingPowerPercent, (Option)security),
SecurityType.IndexOption => new OptionMarginModel(RequiredFreeBuyingPowerPercent),
SecurityType.Option => new OptionMarginModel(RequiredFreeBuyingPowerPercent),
_ => AccountType == AccountType.Cash
? new CashBuyingPowerModel()
: new SecurityMarginModel(GetLeverage(security), RequiredFreeBuyingPowerPercent)
};
}
/// <summary>

View File

@@ -0,0 +1,132 @@
/*
* 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.Reflection;
using System.Reflection.Emit;
using QuantConnect.Benchmarks;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Securities;
using static QuantConnect.StringExtensions;
using static QuantConnect.Util.SecurityExtensions;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Exante Brokerage Model Implementation for Back Testing.
/// </summary>
public class ExanteBrokerageModel : DefaultBrokerageModel
{
private const decimal EquityLeverage = 1.2m;
/// <summary>
/// Constructor for Exante brokerage model
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public ExanteBrokerageModel(AccountType accountType = AccountType.Cash)
: base(accountType)
{
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("SPY", SecurityType.Equity, Market.USA);
return SecurityBenchmark.CreateInstance(securities, symbol);
}
/// <summary>
/// Returns true if the brokerage could accept this order. This takes into account
/// order type, security type, and order size limits.
/// </summary>
/// <remarks>
/// For example, a brokerage may have no connectivity at certain times, or an order rate/size limit
/// </remarks>
/// <param name="security">The security being ordered</param>
/// <param name="order">The order to be processed</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be submitted</param>
/// <returns>True if the brokerage could process the order, false otherwise</returns>
public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message)
{
message = null;
if (order == null)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Invariant($"Order is null.")
);
return false;
}
if (order.Price == 0m)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Invariant($"Price is not set.")
);
return false;
}
if (security.Type != SecurityType.Forex &&
security.Type != SecurityType.Equity &&
security.Type != SecurityType.Index &&
security.Type != SecurityType.Option &&
security.Type != SecurityType.Future &&
security.Type != SecurityType.Cfd &&
security.Type != SecurityType.Crypto &&
security.Type != SecurityType.Index)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Invariant(
$"The {nameof(ExanteBrokerageModel)} does not support {security.Type} security type.")
);
return false;
}
return true;
}
/// <summary>
/// Gets a new fee model that represents this brokerage's fee structure
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public override IFeeModel GetFeeModel(Security security) => new ExanteFeeModel();
/// <summary>
/// Exante global leverage rule
/// </summary>
/// <param name="security">The security's whose leverage we seek</param>
/// <returns>The leverage for the specified security</returns>
public override decimal GetLeverage(Security security)
{
if (AccountType == AccountType.Cash || security.IsInternalFeed() || security.Type == SecurityType.Base)
{
return 1m;
}
return security.Type switch
{
SecurityType.Forex => 1.05m,
SecurityType.Equity => EquityLeverage,
_ => 1.0m,
};
}
}
}

View File

@@ -47,20 +47,6 @@ namespace QuantConnect.Brokerages
{
}
/// <summary>
/// Gets a new buying power model for the security, returning the default model with the security's configured leverage.
/// For cash accounts, leverage = 1 is used.
/// For margin trading, FTX supports up to 20x leverage, default is 3xs
/// </summary>
/// <param name="security">The security to get a buying power model for</param>
/// <returns>The buying power model for this brokerage/security</returns>
public override IBuyingPowerModel GetBuyingPowerModel(Security security)
{
return AccountType == AccountType.Margin
? new SecurityMarginModel(GetLeverage(security))
: new CashBuyingPowerModel();
}
/// <summary>
/// Gets the brokerage's leverage for the specified security
/// </summary>

View File

@@ -207,6 +207,9 @@ namespace QuantConnect.Brokerages
case BrokerageName.Binance:
return new BinanceBrokerageModel(accountType);
case BrokerageName.BinanceUS:
return new BinanceUSBrokerageModel(accountType);
case BrokerageName.GDAX:
return new GDAXBrokerageModel(accountType);
@@ -227,6 +230,9 @@ namespace QuantConnect.Brokerages
case BrokerageName.Kraken:
return new KrakenBrokerageModel(accountType);
case BrokerageName.Exante:
return new ExanteBrokerageModel(accountType);
case BrokerageName.FTX:
return new FTXBrokerageModel(accountType);

View File

@@ -133,23 +133,6 @@ namespace QuantConnect.Brokerages
return new KrakenFeeModel();
}
/// <summary>
/// Gets a new buying power model for the security, returning the default model with the security's configured leverage.
/// For cash accounts, leverage = 1 is used.
/// For margin trading, max leverage = 5
/// </summary>
/// <param name="security">The security to get a buying power model for</param>
/// <returns>The buying power model for this brokerage/security</returns>
public override IBuyingPowerModel GetBuyingPowerModel(Security security)
{
if (AccountType == AccountType.Margin)
{
return new SecurityMarginModel(GetLeverage(security));
}
return new CashBuyingPowerModel();
}
/// <summary>
/// Kraken global leverage rule
/// </summary>

View File

@@ -38,7 +38,7 @@ namespace QuantConnect.Brokerages
typeof(GoodTilDateTimeInForce)
};
private const decimal _maxLeverage = 7m;
private const decimal _maxLeverage = 5m;
/// <summary>
/// Initializes a new instance of the <see cref="SamcoBrokerageModel"/> class
@@ -141,20 +141,6 @@ namespace QuantConnect.Brokerages
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets();
/// <summary>
/// Gets a new buying power model for the security, returning the default model with the
/// security's configured leverage. For cash accounts, leverage = 1 is used. For margin
/// trading, max leverage = 7
/// </summary>
/// <param name="security">The security to get a buying power model for</param>
/// <returns>The buying power model for this brokerage/security</returns>
public override IBuyingPowerModel GetBuyingPowerModel(Security security)
{
return AccountType == AccountType.Cash
? (IBuyingPowerModel)new CashBuyingPowerModel()
: new SecurityMarginModel(_maxLeverage);
}
/// <summary>
/// Samco global leverage rule
/// </summary>

View File

@@ -1,4 +1,4 @@
/*
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
@@ -34,7 +34,7 @@ namespace QuantConnect.Brokerages
/// <summary>
/// Initializes a new instance of the <see cref="DefaultBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modelled, defaults to
/// <param name="accountType">The type of account to be modeled, defaults to
/// <see cref="QuantConnect.AccountType.Margin"/></param>
public TradierBrokerageModel(AccountType accountType = AccountType.Margin)
: base(accountType)
@@ -102,7 +102,7 @@ namespace QuantConnect.Brokerages
if (request.Quantity != null && request.Quantity != order.Quantity)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "UpdateRejected",
"Traider does not support updating order quantities."
"Tradier does not support updating order quantities."
);
return false;

View File

@@ -38,7 +38,7 @@ namespace QuantConnect.Brokerages
typeof(GoodTilDateTimeInForce)
};
private const decimal _maxLeverage = 7m;
private const decimal _maxLeverage = 5m;
/// <summary>
/// Initializes a new instance of the <see cref="ZerodhaBrokerageModel"/> class
@@ -136,22 +136,6 @@ namespace QuantConnect.Brokerages
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets();
/// <summary>
/// Gets a new buying power model for the security, returning the default model with the security's configured leverage.
/// For cash accounts, leverage = 1 is used.
/// For margin trading, max leverage = 7
/// </summary>
/// <param name="security">The security to get a buying power model for</param>
/// <returns>The buying power model for this brokerage/security</returns>
public override IBuyingPowerModel GetBuyingPowerModel(Security security)
{
return AccountType == AccountType.Cash
? (IBuyingPowerModel)new CashBuyingPowerModel()
: new SecurityMarginModel(_maxLeverage);
}
/// <summary>
/// Zerodha global leverage rule
/// </summary>

View File

@@ -15,6 +15,7 @@
using System;
using QuantConnect.Securities;
using System.Collections.Generic;
namespace QuantConnect.Data
{
@@ -51,6 +52,11 @@ namespace QuantConnect.Data
/// </summary>
public SecurityExchangeHours ExchangeHours { get; }
/// <summary>
/// Gets the tradable days specified by this request, in the security's data time zone
/// </summary>
public abstract IEnumerable<DateTime> TradableDays { get; }
/// <summary>
/// Initializes the base data request
/// </summary>

View File

@@ -17,6 +17,7 @@
using System;
using NodaTime;
using QuantConnect.Securities;
using System.Collections.Generic;
namespace QuantConnect.Data
{
@@ -94,6 +95,15 @@ namespace QuantConnect.Data
/// </summary>
public uint ContractDepthOffset { get; }
/// <summary>
/// Gets the tradable days specified by this request, in the security's data time zone
/// </summary>
public override IEnumerable<DateTime> TradableDays => Time.EachTradeableDayInTimeZone(ExchangeHours,
StartTimeLocal,
EndTimeLocal,
DataTimeZone,
IncludeExtendedMarketHours);
/// <summary>
/// Initializes a new instance of the <see cref="HistoryRequest"/> class from the specified parameters
/// </summary>

View File

@@ -1,4 +1,4 @@
/*
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
@@ -29,23 +29,24 @@ namespace QuantConnect.Data
/// </summary>
public class Slice : ExtendedDictionary<dynamic>, IEnumerable<KeyValuePair<Symbol, BaseData>>
{
private readonly Ticks _ticks;
private readonly TradeBars _bars;
private readonly QuoteBars _quoteBars;
private readonly OptionChains _optionChains;
private readonly FuturesChains _futuresChains;
private Ticks _ticks;
private TradeBars _bars;
private QuoteBars _quoteBars;
private OptionChains _optionChains;
private FuturesChains _futuresChains;
// aux data
private readonly Splits _splits;
private readonly Dividends _dividends;
private readonly Delistings _delistings;
private readonly SymbolChangedEvents _symbolChangedEvents;
private Splits _splits;
private Dividends _dividends;
private Delistings _delistings;
private SymbolChangedEvents _symbolChangedEvents;
// string -> data for non-tick data
// string -> list{data} for tick data
private readonly Lazy<DataDictionary<SymbolData>> _data;
private Lazy<DataDictionary<SymbolData>> _data;
// UnlinkedData -> DataDictonary<UnlinkedData>
private Dictionary<Type, object> _dataByType;
private List<BaseData> _rawDataList;
/// <summary>
/// Gets the timestamp for this slice of data
@@ -55,6 +56,14 @@ namespace QuantConnect.Data
get; private set;
}
/// <summary>
/// Gets the timestamp for this slice of data in UTC
/// </summary>
public DateTime UtcTime
{
get; private set;
}
/// <summary>
/// Gets whether or not this slice has data
/// </summary>
@@ -190,8 +199,9 @@ namespace QuantConnect.Data
/// </summary>
/// <param name="time">The timestamp for this slice of data</param>
/// <param name="data">The raw data in this slice</param>
public Slice(DateTime time, IEnumerable<BaseData> data)
: this(time, data.ToList())
/// <param name="utcTime">The timestamp for this slice of data in UTC</param>
public Slice(DateTime time, IEnumerable<BaseData> data, DateTime utcTime)
: this(time, data.ToList(), utcTime: utcTime)
{
}
@@ -202,7 +212,8 @@ namespace QuantConnect.Data
/// </summary>
/// <param name="time">The timestamp for this slice of data</param>
/// <param name="data">The raw data in this slice</param>
public Slice(DateTime time, List<BaseData> data)
/// <param name="utcTime">The timestamp for this slice of data in UTC</param>
public Slice(DateTime time, List<BaseData> data, DateTime utcTime)
: this(time, data, CreateCollection<TradeBars, TradeBar>(time, data),
CreateCollection<QuoteBars, QuoteBar>(time, data),
CreateTicksCollection(time, data),
@@ -211,7 +222,8 @@ namespace QuantConnect.Data
CreateCollection<Splits, Split>(time, data),
CreateCollection<Dividends, Dividend>(time, data),
CreateCollection<Delistings, Delisting>(time, data),
CreateCollection<SymbolChangedEvents, SymbolChangedEvent>(time, data))
CreateCollection<SymbolChangedEvents, SymbolChangedEvent>(time, data),
utcTime: utcTime)
{
}
@@ -223,7 +235,8 @@ namespace QuantConnect.Data
protected Slice(Slice slice)
{
Time = slice.Time;
UtcTime = slice.UtcTime;
_rawDataList = slice._rawDataList;
_dataByType = slice._dataByType;
_data = slice._data;
@@ -257,13 +270,15 @@ namespace QuantConnect.Data
/// <param name="dividends">The dividends for this slice</param>
/// <param name="delistings">The delistings for this slice</param>
/// <param name="symbolChanges">The symbol changed events for this slice</param>
/// <param name="utcTime">The timestamp for this slice of data in UTC</param>
/// <param name="hasData">true if this slice contains data</param>
public Slice(DateTime time, IEnumerable<BaseData> data, TradeBars tradeBars, QuoteBars quoteBars, Ticks ticks, OptionChains optionChains, FuturesChains futuresChains, Splits splits, Dividends dividends, Delistings delistings, SymbolChangedEvents symbolChanges, bool? hasData = null)
public Slice(DateTime time, List<BaseData> data, TradeBars tradeBars, QuoteBars quoteBars, Ticks ticks, OptionChains optionChains, FuturesChains futuresChains, Splits splits, Dividends dividends, Delistings delistings, SymbolChangedEvents symbolChanges, DateTime utcTime, bool? hasData = null)
{
Time = time;
UtcTime = utcTime;
_rawDataList = data;
// market data
_data = new Lazy<DataDictionary<SymbolData>>(() => CreateDynamicDataDictionary(data));
_data = new Lazy<DataDictionary<SymbolData>>(() => CreateDynamicDataDictionary(_rawDataList));
HasData = hasData ?? _data.Value.Count > 0;
@@ -340,7 +355,7 @@ namespace QuantConnect.Data
var requestedOpenInterest = type == typeof(OpenInterest);
if (type == typeof(Tick) || requestedOpenInterest)
{
var dataDictionaryCache = GenericDataDictionary.Get(type);
var dataDictionaryCache = GenericDataDictionary.Get(type, isPythonData: false);
dictionary = Activator.CreateInstance(dataDictionaryCache.GenericType);
foreach (var data in instance.Ticks)
@@ -390,10 +405,23 @@ namespace QuantConnect.Data
}
else
{
var dataDictionaryCache = GenericDataDictionary.Get(type);
var isPythonData = type.IsAssignableTo(typeof(PythonData));
var dataDictionaryCache = GenericDataDictionary.Get(type, isPythonData);
dictionary = Activator.CreateInstance(dataDictionaryCache.GenericType);
foreach (var data in instance._data.Value.Values.Select(x => x.Custom).Where(o => o != null && o.GetType() == type))
foreach (var data in instance._data.Value.Values.Select(x => x.Custom).Where(o =>
{
if (o == null)
{
return false;
}
if (isPythonData && o is PythonData data)
{
return data.IsOfType(type);
}
return o.GetType() == type;
}))
{
dataDictionaryCache.MethodInfo.Invoke(dictionary, new object[] { data.Symbol, data });
}
@@ -444,6 +472,65 @@ namespace QuantConnect.Data
return false;
}
/// <summary>
/// Merge two slice with same Time
/// </summary>
/// <param name="inputSlice">slice instance</param>
/// <remarks> Will change the input collection for re-use</remarks>
public void MergeSlice(Slice inputSlice)
{
if (UtcTime != inputSlice.UtcTime)
{
throw new InvalidOperationException($"Slice with time {UtcTime} can't be merged with given slice with different {inputSlice.UtcTime}");
}
_bars = (TradeBars)UpdateCollection(_bars, inputSlice.Bars);
_quoteBars = (QuoteBars)UpdateCollection(_quoteBars, inputSlice.QuoteBars);
_ticks = (Ticks)UpdateCollection(_ticks, inputSlice.Ticks);
_optionChains = (OptionChains)UpdateCollection(_optionChains, inputSlice.OptionChains);
_futuresChains = (FuturesChains)UpdateCollection(_futuresChains, inputSlice.FuturesChains);
_splits = (Splits)UpdateCollection(_splits, inputSlice.Splits);
_dividends = (Dividends)UpdateCollection(_dividends, inputSlice.Dividends);
_delistings = (Delistings)UpdateCollection(_delistings, inputSlice.Delistings);
_symbolChangedEvents = (SymbolChangedEvents)UpdateCollection(_symbolChangedEvents, inputSlice.SymbolChangedEvents);
if (inputSlice._rawDataList.Count != 0)
{
if (_rawDataList.Count == 0)
{
_rawDataList = inputSlice._rawDataList;
_data = inputSlice._data;
}
else
{
// Should keep this._rawDataList last so that selected data points are not overriden
// while creating _data
inputSlice._rawDataList.AddRange(_rawDataList);
_rawDataList = inputSlice._rawDataList;
_data = new Lazy<DataDictionary<SymbolData>>(() => CreateDynamicDataDictionary(_rawDataList));
}
}
}
private static DataDictionary<T> UpdateCollection<T>(DataDictionary<T> baseCollection, DataDictionary<T> inputCollection)
{
if (baseCollection == null || baseCollection.Count == 0)
{
return inputCollection;
}
if (inputCollection?.Count > 0)
{
foreach (var kvp in inputCollection)
{
if (!baseCollection.ContainsKey(kvp.Key))
{
baseCollection.Add(kvp.Key, kvp.Value);
}
}
}
return baseCollection;
}
/// <summary>
/// Produces the dynamic data dictionary from the input data
/// </summary>
@@ -653,15 +740,22 @@ namespace QuantConnect.Data
/// <summary>
/// Provides a <see cref="GenericDataDictionary"/> instance for a given <see cref="Type"/>
/// </summary>
/// <param name="type"></param>
/// <param name="type">The requested data type</param>
/// <param name="isPythonData">True if data is of <see cref="PythonData"/> type</param>
/// <returns>A new instance or retrieved from the cache</returns>
public static GenericDataDictionary Get(Type type)
public static GenericDataDictionary Get(Type type, bool isPythonData)
{
GenericDataDictionary dataDictionaryCache;
if (!_genericCache.TryGetValue(type, out dataDictionaryCache))
{
var generic = typeof(DataDictionary<>).MakeGenericType(type);
var method = generic.GetMethod("Add", new[] { typeof(Symbol), type });
var dictionaryType = type;
if (isPythonData)
{
// let's create a python data dictionary because the data itself will be a PythonData type in C#
dictionaryType = typeof(PythonData);
}
var generic = typeof(DataDictionary<>).MakeGenericType(dictionaryType);
var method = generic.GetMethod("Add", new[] { typeof(Symbol), dictionaryType });
_genericCache[type] = dataDictionaryCache = new GenericDataDictionary(generic, method);
}

View File

@@ -47,18 +47,11 @@ namespace QuantConnect.Data.UniverseSelection
/// <summary>
/// Gets the tradable days specified by this request, in the security's data time zone
/// </summary>
public IEnumerable<DateTime> TradableDays
{
get
{
return Time.EachTradeableDayInTimeZone(Security.Exchange.Hours,
StartTimeLocal,
EndTimeLocal,
Configuration.DataTimeZone,
Configuration.ExtendedMarketHours
);
}
}
public override IEnumerable<DateTime> TradableDays => Time.EachTradeableDayInTimeZone(Security.Exchange.Hours,
StartTimeLocal,
EndTimeLocal,
Configuration.DataTimeZone,
Configuration.ExtendedMarketHours);
/// <summary>
/// Initializes a new instance of the <see cref="SubscriptionRequest"/> class
@@ -105,4 +98,4 @@ namespace QuantConnect.Data.UniverseSelection
{
}
}
}
}

View File

@@ -348,4 +348,4 @@ namespace QuantConnect
return GetValues.ToPyList();
}
}
}
}

View File

@@ -174,10 +174,18 @@ namespace QuantConnect
/// Helper method to download a provided url as a string
/// </summary>
/// <param name="url">The url to download data from</param>
public static string DownloadData(this string url)
/// <param name="headers">Add custom headers for the request</param>
public static string DownloadData(this string url, Dictionary<string, string> headers = null)
{
using (var client = new HttpClient())
{
if (headers != null)
{
foreach (var kvp in headers)
{
client.DefaultRequestHeaders.Add(kvp.Key, kvp.Value);
}
}
try
{
using (var response = client.GetAsync(url).Result)
@@ -1634,6 +1642,45 @@ namespace QuantConnect
return rounded;
}
/// <summary>
/// Helper method to determine if a specific market is open
/// </summary>
/// <param name="security">The target security</param>
/// <param name="extendedMarketHours">True if should consider extended market hours</param>
/// <returns>True if the market is open</returns>
public static bool IsMarketOpen(this Security security, bool extendedMarketHours)
{
if (!security.Exchange.Hours.IsOpen(security.LocalTime, extendedMarketHours))
{
// if we're not open at the current time exactly, check the bar size, this handle large sized bars (hours/days)
var currentBar = security.GetLastData();
if (currentBar == null
|| security.LocalTime.Date != currentBar.EndTime.Date
|| !security.Exchange.IsOpenDuringBar(currentBar.Time, currentBar.EndTime, extendedMarketHours))
{
return false;
}
}
return true;
}
/// <summary>
/// Helper method to determine if a specific market is open
/// </summary>
/// <param name="symbol">The target symbol</param>
/// <param name="utcTime">The current UTC time</param>
/// <param name="extendedMarketHours">True if should consider extended market hours</param>
/// <returns>True if the market is open</returns>
public static bool IsMarketOpen(this Symbol symbol, DateTime utcTime, bool extendedMarketHours)
{
var exchangeHours = MarketHoursDatabase.FromDataFolder()
.GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType);
var time = utcTime.ConvertFromUtc(exchangeHours.TimeZone);
return exchangeHours.IsOpen(time, extendedMarketHours);
}
/// <summary>
/// Extension method to round a datetime to the nearest unit timespan.
/// </summary>
@@ -2857,15 +2904,12 @@ namespace QuantConnect
PythonActivator pythonType;
if (!PythonActivators.TryGetValue(pyObject.Handle, out pythonType))
{
AssemblyName an;
using (Py.GIL())
{
an = new AssemblyName(pyObject.Repr().Split('\'')[1]);
}
var assemblyName = pyObject.GetAssemblyName();
var typeBuilder = AssemblyBuilder
.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run)
.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run)
.DefineDynamicModule("MainModule")
.DefineType(an.Name, TypeAttributes.Class, type);
// creating the type as public is required to allow 'dynamic' to be able to bind at runtime
.DefineType(assemblyName.Name, TypeAttributes.Class | TypeAttributes.Public, type);
pythonType = new PythonActivator(typeBuilder.CreateType(), pyObject);
@@ -2877,6 +2921,19 @@ namespace QuantConnect
return pythonType.Type;
}
/// <summary>
/// Helper method to get the assembly name from a python type
/// </summary>
/// <param name="pyObject">Python object pointing to the python type. <see cref="PyObject.GetPythonType"/></param>
/// <returns>The python type assembly name</returns>
public static AssemblyName GetAssemblyName(this PyObject pyObject)
{
using (Py.GIL())
{
return new AssemblyName(pyObject.Repr().Split('\'')[1]);
}
}
/// <summary>
/// Performs on-line batching of the specified enumerator, emitting chunks of the requested batch size
/// </summary>

View File

@@ -63,7 +63,8 @@ namespace QuantConnect
Tuple.Create(CFE, 33),
Tuple.Create(FTX, 34),
Tuple.Create(FTXUS, 35)
Tuple.Create(FTXUS, 35),
Tuple.Create(BinanceUS, 36)
};
static Market()
@@ -218,6 +219,10 @@ namespace QuantConnect
/// </summary>
public const string FTXUS = "ftxus";
/// <summary>
/// Binance.US
/// </summary>
public const string BinanceUS = "binanceus";
/// <summary>
/// Adds the specified market to the map of available markets with the specified identifier.

View File

@@ -13,6 +13,7 @@
* limitations under the License.
*/
using QuantConnect.Util;
using QuantConnect.Securities;
namespace QuantConnect.Orders.Fees
@@ -58,7 +59,8 @@ namespace QuantConnect.Orders.Fees
var security = parameters.Security;
var order = parameters.Order;
decimal fee = _takerFee;
// apply fee factor, currently we do not model 30-day volume, so we use the first tier
var fee = _takerFee;
var props = order.Properties as BinanceOrderProperties;
if (order.Type == OrderType.Limit &&
@@ -68,6 +70,13 @@ namespace QuantConnect.Orders.Fees
fee = _makerFee;
}
if (order.Direction == OrderDirection.Buy)
{
// fees taken in the received currency
CurrencyPairUtil.DecomposeCurrencyPair(order.Symbol, out var baseCurrency, out _);
return new OrderFee(new CashAmount(order.AbsoluteQuantity * fee, baseCurrency));
}
// get order value in quote currency
var unitPrice = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
if (order.Type == OrderType.Limit)
@@ -78,7 +87,6 @@ namespace QuantConnect.Orders.Fees
unitPrice *= security.SymbolProperties.ContractMultiplier;
// apply fee factor, currently we do not model 30-day volume, so we use the first tier
return new OrderFee(new CashAmount(
unitPrice * order.AbsoluteQuantity * fee,
security.QuoteCurrency.Symbol));

View File

@@ -13,6 +13,7 @@
* limitations under the License.
*/
using QuantConnect.Util;
using QuantConnect.Securities;
namespace QuantConnect.Orders.Fees
@@ -46,7 +47,8 @@ namespace QuantConnect.Orders.Fees
{
var order = parameters.Order;
var security = parameters.Security;
decimal fee = TakerFee;
// apply fee factor, currently we do not model 30-day volume, so we use the first tier
var fee = TakerFee;
var props = order.Properties as BitfinexOrderProperties;
if (order.Type == OrderType.Limit &&
@@ -57,6 +59,13 @@ namespace QuantConnect.Orders.Fees
fee = MakerFee;
}
if (order.Direction == OrderDirection.Buy)
{
// fees taken in the received currency
CurrencyPairUtil.DecomposeCurrencyPair(order.Symbol, out var baseCurrency, out _);
return new OrderFee(new CashAmount(order.AbsoluteQuantity * fee, baseCurrency));
}
// get order value in quote currency
var unitPrice = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
if (order.Type == OrderType.Limit)
@@ -67,7 +76,6 @@ namespace QuantConnect.Orders.Fees
unitPrice *= security.SymbolProperties.ContractMultiplier;
// apply fee factor, currently we do not model 30-day volume, so we use the first tier
return new OrderFee(new CashAmount(
unitPrice * order.AbsoluteQuantity * fee,
security.QuoteCurrency.Symbol));

View File

@@ -0,0 +1,124 @@
/*
* 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.Securities;
using static QuantConnect.StringExtensions;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models Exante order fees.
/// According to:
/// <list type="bullet">
/// <item>https://support.exante.eu/hc/en-us/articles/115005873143-Fees-overview-exchange-imposed-fees?source=search</item>
/// <item>https://exante.eu/markets/</item>
/// </list>
/// </summary>
public class ExanteFeeModel : FeeModel
{
public const decimal MarketUsaRate = 0.02m;
public const decimal DefaultRate = 0.02m;
private readonly decimal _forexCommissionRate;
/// <summary>
/// Creates a new instance
/// </summary>
/// <param name="forexCommissionRate">Commission rate for FX operations</param>
public ExanteFeeModel(decimal forexCommissionRate = 0.25m)
{
_forexCommissionRate = forexCommissionRate;
}
/// <summary>
/// Gets the order fee associated with the specified order.
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in a <see cref="CashAmount"/> instance</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
var order = parameters.Order;
var security = parameters.Security;
decimal feeResult;
string feeCurrency;
switch (security.Type)
{
case SecurityType.Forex:
var totalOrderValue = order.GetValue(security);
feeResult = Math.Abs(_forexCommissionRate * totalOrderValue);
feeCurrency = Currencies.USD;
break;
case SecurityType.Equity:
var equityFee = ComputeEquityFee(order);
feeResult = equityFee.Amount;
feeCurrency = equityFee.Currency;
break;
case SecurityType.Option:
case SecurityType.IndexOption:
var optionsFee = ComputeOptionFee(order);
feeResult = optionsFee.Amount;
feeCurrency = optionsFee.Currency;
break;
case SecurityType.Future:
case SecurityType.FutureOption:
feeResult = 1.5m;
feeCurrency = Currencies.USD;
break;
default:
throw new ArgumentException(Invariant($"Unsupported security type: {security.Type}"));
}
return new OrderFee(new CashAmount(feeResult, feeCurrency));
}
/// <summary>
/// Computes fee for equity order
/// </summary>
/// <param name="order">LEAN order</param>
private static CashAmount ComputeEquityFee(Order order)
{
switch (order.Symbol.ID.Market)
{
case Market.USA:
return new CashAmount(order.AbsoluteQuantity * MarketUsaRate, Currencies.USD);
default:
return new CashAmount(order.AbsoluteQuantity * order.Price * DefaultRate, Currencies.USD);
}
}
/// <summary>
/// Computes fee for option order
/// </summary>
/// <param name="order">LEAN order</param>
private static CashAmount ComputeOptionFee(Order order)
{
return order.Symbol.ID.Market switch
{
Market.USA => new CashAmount(order.AbsoluteQuantity * 1.5m, Currencies.USD),
_ =>
// ToDo: clarify the value for different exchanges
throw new ArgumentException(Invariant($"Unsupported exchange: ${order.Symbol.ID.Market}"))
};
}
}
}

View File

@@ -18,7 +18,8 @@ using QuantConnect.Securities;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// An order fee where the fee quantity has already been subtracted from the filled quantity
/// An order fee where the fee quantity has already been subtracted from the filled quantity so instead we subtracted
/// from the quote currency when applied to the portfolio
/// </summary>
/// <remarks>
/// This type of order fee is returned by some crypto brokerages (e.g. Bitfinex and Binance)
@@ -26,13 +27,20 @@ namespace QuantConnect.Orders.Fees
/// </remarks>
public class ModifiedFillQuantityOrderFee : OrderFee
{
private readonly string _quoteCurrency;
private readonly decimal _contractMultiplier;
/// <summary>
/// Initializes a new instance of the <see cref="ModifiedFillQuantityOrderFee"/> class
/// </summary>
/// <param name="orderFee">The order fee</param>
public ModifiedFillQuantityOrderFee(CashAmount orderFee)
/// <param name="quoteCurrency">The associated security quote currency</param>
/// <param name="contractMultiplier">The associated security contract multiplier</param>
public ModifiedFillQuantityOrderFee(CashAmount orderFee, string quoteCurrency, decimal contractMultiplier)
: base(orderFee)
{
_quoteCurrency = quoteCurrency;
_contractMultiplier = contractMultiplier;
}
/// <summary>
@@ -42,7 +50,7 @@ namespace QuantConnect.Orders.Fees
/// <param name="fill">The order fill event</param>
public override void ApplyToPortfolio(SecurityPortfolioManager portfolio, OrderEvent fill)
{
// do not apply the fee twice
portfolio.CashBook[_quoteCurrency].AddAmount(-Value.Amount * fill.FillPrice * _contractMultiplier);
}
}
}

View File

@@ -23,7 +23,7 @@ namespace QuantConnect.Orders.Fees
/// <summary>
/// Brokerage calculation Factor
/// </summary>
protected override decimal BrokerageMultiplier => 0.0003M;
protected override decimal BrokerageMultiplier => 0.0002M;
/// <summary>
/// Maximum brokerage per order
@@ -38,7 +38,7 @@ namespace QuantConnect.Orders.Fees
/// <summary>
/// Exchange Transaction Charge calculation Factor
/// </summary>
protected override decimal ExchangeTransactionChargeMultiplier => 0.0000325M;
protected override decimal ExchangeTransactionChargeMultiplier => 0.0000345M;
/// <summary>
/// State Tax calculation Factor
@@ -48,11 +48,11 @@ namespace QuantConnect.Orders.Fees
/// <summary>
/// Sebi Charges calculation Factor
/// </summary>
protected override decimal SebiChargesMultiplier => 0.000002M;
protected override decimal SebiChargesMultiplier => 0.000001M;
/// <summary>
/// Stamp Charges calculation Factor
/// </summary>
protected override decimal StampChargesMultiplier => 0.00002M;
protected override decimal StampChargesMultiplier => 0.00003M;
}
}

View File

@@ -790,17 +790,7 @@ namespace QuantConnect.Orders.Fills
/// </summary>
protected static bool IsExchangeOpen(Security asset, bool isExtendedMarketHours)
{
if (!asset.Exchange.DateTimeIsOpen(asset.LocalTime))
{
// if we're not open at the current time exactly, check the bar size, this handle large sized bars (hours/days)
var currentBar = asset.GetLastData();
if (asset.LocalTime.Date != currentBar.EndTime.Date
|| !asset.Exchange.IsOpenDuringBar(currentBar.Time, currentBar.EndTime, isExtendedMarketHours))
{
return false;
}
}
return true;
return asset.IsMarketOpen(isExtendedMarketHours);
}
}
}

View File

@@ -202,14 +202,11 @@ namespace QuantConnect.Orders
protected Order()
{
Time = new DateTime();
Price = 0;
PriceCurrency = string.Empty;
Quantity = 0;
Symbol = Symbol.Empty;
Status = OrderStatus.None;
Tag = "";
Tag = string.Empty;
BrokerId = new List<string>();
ContingentId = 0;
Properties = new OrderProperties();
}
@@ -224,14 +221,12 @@ namespace QuantConnect.Orders
protected Order(Symbol symbol, decimal quantity, DateTime time, string tag = "", IOrderProperties properties = null)
{
Time = time;
Price = 0;
PriceCurrency = string.Empty;
Quantity = quantity;
Symbol = symbol;
Status = OrderStatus.None;
Tag = tag;
BrokerId = new List<string>();
ContingentId = 0;
Properties = properties ?? new OrderProperties();
}

View File

@@ -26,6 +26,7 @@ namespace QuantConnect.Python
/// </summary>
public class PythonData : DynamicData
{
private readonly string _pythonTypeName;
private readonly dynamic _pythonData;
private readonly dynamic _defaultResolution;
private readonly dynamic _supportedResolutions;
@@ -54,6 +55,7 @@ namespace QuantConnect.Python
_isSparseData = pythonData.GetPythonMethod("IsSparseData");
_defaultResolution = pythonData.GetPythonMethod("DefaultResolution");
_supportedResolutions = pythonData.GetPythonMethod("SupportedResolutions");
_pythonTypeName = pythonData.GetPythonType().GetAssemblyName().Name;
}
}
@@ -86,7 +88,11 @@ namespace QuantConnect.Python
using (Py.GIL())
{
var data = _pythonData.Reader(config, line, date, isLiveMode);
return (data as PyObject).GetAndDispose<BaseData>();
var result = (data as PyObject).GetAndDispose<BaseData>();
(result as PythonData)?.SetProperty("__typename", _pythonTypeName);
return result;
}
}
@@ -174,5 +180,19 @@ namespace QuantConnect.Python
SetProperty(index, value is double ? value.ConvertInvariant<decimal>() : value);
}
}
/// <summary>
/// Helper method to determine if the current instance is of the provided type
/// </summary>
/// <param name="type">Target type to check against</param>
/// <returns>True if this instance is of the provided type</returns>
public bool IsOfType(Type type)
{
if (HasProperty("__typename"))
{
return (string)GetProperty("__typename") == type.FullName;
}
return GetType() == type;
}
}
}
}

View File

@@ -243,7 +243,7 @@ namespace QuantConnect.Securities.Future
var marginRequirementsEntries = _dataProvider.ReadLines(file)
.Where(x => !x.StartsWith("#") && !string.IsNullOrWhiteSpace(x))
.Skip(1)
.Select(FromCsvLine)
.Select(MarginRequirementsEntry.Create)
.OrderBy(x => x.Date)
.ToArray();
@@ -261,87 +261,5 @@ namespace QuantConnect.Securities.Future
return marginRequirementsEntries;
}
}
/// <summary>
/// Creates a new instance of <see cref="MarginRequirementsEntry"/> from the specified csv line
/// </summary>
/// <param name="csvLine">The csv line to be parsed</param>
/// <returns>A new <see cref="MarginRequirementsEntry"/> for the specified csv line</returns>
private MarginRequirementsEntry FromCsvLine(string csvLine)
{
var line = csvLine.Split(',');
DateTime date;
if (!DateTime.TryParseExact(line[0], DateFormat.EightCharacter, CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
{
Log.Trace($"Couldn't parse date/time while reading future margin requirement file. Line: {csvLine}");
}
decimal initialOvernight;
if (!decimal.TryParse(line[1], out initialOvernight))
{
Log.Trace($"Couldn't parse Initial Overnight margin requirements while reading future margin requirement file. Line: {csvLine}");
}
decimal maintenanceOvernight;
if (!decimal.TryParse(line[2], out maintenanceOvernight))
{
Log.Trace($"Couldn't parse Maintenance Overnight margin requirements while reading future margin requirement file. Line: {csvLine}");
}
// default value, if present in file we try to parse
decimal initialIntraday = initialOvernight * 0.4m;
if (line.Length >= 4
&& !decimal.TryParse(line[3], out initialIntraday))
{
Log.Trace($"Couldn't parse Initial Intraday margin requirements while reading future margin requirement file. Line: {csvLine}");
}
// default value, if present in file we try to parse
decimal maintenanceIntraday = maintenanceOvernight * 0.4m;
if (line.Length >= 5
&& !decimal.TryParse(line[4], out maintenanceIntraday))
{
Log.Trace($"Couldn't parse Maintenance Intraday margin requirements while reading future margin requirement file. Line: {csvLine}");
}
return new MarginRequirementsEntry
{
Date = date,
InitialOvernight = initialOvernight,
MaintenanceOvernight = maintenanceOvernight,
InitialIntraday = initialIntraday,
MaintenanceIntraday = maintenanceIntraday
};
}
// Private POCO class for modeling margin requirements at given date
class MarginRequirementsEntry
{
/// <summary>
/// Date of margin requirements change
/// </summary>
public DateTime Date;
/// <summary>
/// Initial overnight margin for the contract effective from the date of change
/// </summary>
public decimal InitialOvernight;
/// <summary>
/// Maintenance overnight margin for the contract effective from the date of change
/// </summary>
public decimal MaintenanceOvernight;
/// <summary>
/// Initial intraday margin for the contract effective from the date of change
/// </summary>
public decimal InitialIntraday;
/// <summary>
/// Maintenance intraday margin for the contract effective from the date of change
/// </summary>
public decimal MaintenanceIntraday;
}
}
}

View File

@@ -1,4 +1,4 @@
/*
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
@@ -229,6 +229,96 @@ namespace QuantConnect.Securities
/// </summary>
/// <returns>The symbol</returns>
public const string JapaneseYenEmini = "J7";
/// <summary>
/// Micro EUR/USD Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroEUR = "M6E";
/// <summary>
/// Micro AUD/USD Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroAUD = "M6A";
/// <summary>
/// Micro GBP/USD Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroGBP = "M6B";
/// <summary>
/// Micro CAD/USD Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroCADUSD = "MCD";
/// <summary>
/// Micro JPY/USD Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroJPY = "MJY";
/// <summary>
/// Micro CHF/USD Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroCHF = "MSF";
/// <summary>
/// Micro USD/JPY Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroUSDJPY = "M6J";
/// <summary>
/// Micro INR/USD Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroINRUSD = "MIR";
/// <summary>
/// Micro USD/CAD Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroCAD = "M6C";
/// <summary>
/// Micro USD/CHF Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroUSDCHF = "M6S";
/// <summary>
/// Micro USD/CNH Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroUSDCNH = "MNH";
/// <summary>
/// Micro Ether Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroEther = "MET";
/// <summary>
/// Micro Bitcoin Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroBTC = "MBT";
/// <summary>
/// BTIC on Micro Ether Futures
/// </summary>
/// <returns>The symbol</returns>
public const string BTICMicroEther = "MRB";
/// <summary>
/// BTIC on Micro Bitcoin Futures
/// </summary>
/// <returns>The symbol</returns>
public const string BTICMicroBTC = "MIB";
}
/// <summary>
@@ -660,6 +750,54 @@ namespace QuantConnect.Securities
/// </summary>
/// <returns>The symbol</returns>
public const string LowSulfurGasoil = "G";
/// <summary>
/// Micro WTI Crude Oil Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroCrudeOilWTI = "MCL";
/// <summary>
/// Micro Singapore FOB Marine Fuel 0.5% (Platts) Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroSingaporeFOBMarineFuelZeroPointFivePercetPlatts = "S5O";
/// <summary>
/// Micro Gasoil 0.1% Barges FOB ARA (Platts) Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroGasoilZeroPointOnePercentBargesFOBARAPlatts = "M1B";
/// <summary>
/// Micro European FOB Rdam Marine Fuel 0.5% Barges (Platts) Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroEuropeanFOBRdamMarineFuelZeroPointFivePercentBargesPlatts = "R5O";
/// <summary>
/// Micro European 3.5% Fuel Oil Barges FOB Rdam (Platts) Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroEuropeanThreePointFivePercentOilBargesFOBRdamPlatts = "MEF";
/// <summary>
/// Micro Singapore Fuel Oil 380CST (Platts) Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroSingaporeFuelOil380CSTPlatts = "MAF";
/// <summary>
/// Micro Coal (API 5) fob Newcastle (Argus/McCloskey) Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroCoalAPIFivefobNewcastleArgusMcCloskey = "M5F";
/// <summary>
/// Micro European 3.5% Fuel Oil Cargoes FOB Med (Platts) Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroEuropeanThreePointFivePercentFuelOilCargoesFOBMedPlatts = "M35";
}
/// <summary>
@@ -712,6 +850,30 @@ namespace QuantConnect.Securities
/// Ultra 10-Year U.S. Treasury Note Futures
/// </summary>
public const string UltraTenYearUSTreasuryNote = "TN";
/// <summary>
/// Micro 10-Year Yield Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroY10TreasuryNote = "10Y";
/// <summary>
/// Micro 30-Year Yield Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroY30TreasuryBond = "30Y";
/// <summary>
/// Micro 2-Year Yield Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroY2TreasuryBond = "2YY";
/// <summary>
/// Micro 5-Year Yield Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroY5TreasuryBond = "5YY";
}
/// <summary>
@@ -824,7 +986,32 @@ namespace QuantConnect.Securities
/// <summary>
/// Hang Seng Index
/// </summary>
/// <returns>The symbol</returns>
public const string HangSeng = "HSI";
/// <summary>
/// Micro E-mini S&amp;P 500 Index Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroSP500EMini = "MES";
/// <summary>
/// Micro E-mini Nasdaq-100 Index Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroNASDAQ100EMini = "MNQ";
/// <summary>
/// Micro E-mini Russell 2000 Index Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroRussell2000EMini = "M2K";
/// <summary>
/// Micro E-mini Dow Jones Industrial Average Index Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroDow30EMini = "MYM";
}
/// <summary>
@@ -915,6 +1102,30 @@ namespace QuantConnect.Securities
/// </summary>
/// <returns>The symbol</returns>
public const string USMidwestDomesticHotRolledCoilSteelCRUIndex = "HRC";
/// <summary>
/// Micro Gold Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroGold = "MGC";
/// <summary>
/// Micro Silver Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroSilver = "SIL";
/// <summary>
/// Micro Gold TAS Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroGoldTAS = "MGT";
/// <summary>
/// Micro Palladium Futures
/// </summary>
/// <returns>The symbol</returns>
public const string MicroPalladium = "PAM";
}
/// <summary>
@@ -1001,4 +1212,4 @@ namespace QuantConnect.Securities
public const string NonfatDryMilk = "GNF";
}
}
}
}

View File

@@ -1073,12 +1073,10 @@ namespace QuantConnect.Securities.Future
{
// Monthly contracts listed for 6 consecutive months and 2 additional Dec contract months. If the 6 consecutive months includes Dec, list only 1 additional Dec contract month.
// Trading terminates at 4:00 p.m. London time on the last Friday of the contract month. If that day is not a business day in both the U.K. and the US, trading terminates on the preceding day that is a business day for both the U.K. and the U.S..
var lastFriday = (from day in Enumerable.Range(1, DateTime.DaysInMonth(time.Year, time.Month))
where new DateTime(time.Year, time.Month, day).DayOfWeek == DayOfWeek.Friday
select new DateTime(time.Year, time.Month, day)).Last();
var lastFriday =FuturesExpiryUtilityFunctions.LastFriday(time);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.AUDNZD, SecurityType.Future)
.GetEntry(Market.CME, Futures.Currencies.BTC, SecurityType.Future)
.ExchangeHours
.Holidays;
@@ -1940,8 +1938,8 @@ namespace QuantConnect.Securities.Future
}
else
{
var lastBuisnessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(twentyFifth,-1);
return FuturesExpiryUtilityFunctions.AddBusinessDays(lastBuisnessDay,-3);
var lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(twentyFifth,-1);
return FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay,-3);
}
})
},
@@ -2606,6 +2604,762 @@ namespace QuantConnect.Securities.Future
return FuturesExpiryUtilityFunctions.DairyLastTradeDate(time);
})
},
// Micro Gold Futures (MGC): https://www.cmegroup.com/markets/metals/precious/e-micro-gold.contractSpecs.html
{Symbol.Create(Futures.Metals.MicroGold, SecurityType.Future, Market.COMEX), (time =>
{
// Monthly contracts
// Trading terminates on the third last business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 3);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.COMEX, Futures.Metals.MicroGold, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
return lastBusinessDay;
})
},
// Micro Silver Futures (SIL): https://www.cmegroup.com/markets/metals/precious/1000-oz-silver.contractSpecs.html
{Symbol.Create(Futures.Metals.MicroSilver, SecurityType.Future, Market.COMEX), (time =>
{
// Monthly contracts
// Trading terminates on the third last business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 3);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.COMEX, Futures.Metals.MicroSilver, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
return lastBusinessDay;
})
},
// Micro Gold TAS Futures (MGT): https://www.cmegroup.com/markets/metals/precious/e-micro-gold.contractSpecs.html
{Symbol.Create(Futures.Metals.MicroGoldTAS, SecurityType.Future, Market.COMEX), (time =>
{
// Monthly contracts
// Trading terminates on the third last business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 3);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.COMEX, Futures.Metals.MicroGoldTAS, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
return lastBusinessDay;
})
},
// Micro Palladium Futures (PAM): https://www.cmegroup.com/markets/metals/precious/e-micro-palladium.contractSpecs.html
{Symbol.Create(Futures.Metals.MicroPalladium, SecurityType.Future, Market.NYMEX), (time =>
{
// Monthly contracts
// Trading terminates on the third last business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 3);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.NYMEX, Futures.Metals.MicroPalladium, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
return lastBusinessDay;
})
},
// Micro 10-Year Yield Futures (10Y): https://www.cmegroup.com/markets/interest-rates/us-treasury/micro-10-year-yield.contractSpecs.html
{Symbol.Create(Futures.Financials.MicroY10TreasuryNote, SecurityType.Future, Market.CBOT), (time =>
{
// Monthly contracts
// Trading terminates on the last business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CBOT, Futures.Financials.MicroY10TreasuryNote, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
return lastBusinessDay;
})
},
// Micro 30-Year Yield Futures (30Y): https://www.cmegroup.com/markets/interest-rates/us-treasury/micro-30-year-yield_contract_specifications.html
{Symbol.Create(Futures.Financials.MicroY30TreasuryBond, SecurityType.Future, Market.CBOT), (time =>
{
// Monthly contracts
// Trading terminates on the last business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CBOT, Futures.Financials.MicroY30TreasuryBond, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
return lastBusinessDay;
})
},
// Micro 2-Year Yield Futures (2YY): https://www.cmegroup.com/markets/interest-rates/us-treasury/micro-2-year-yield.contractSpecs.html
{Symbol.Create(Futures.Financials.MicroY2TreasuryBond, SecurityType.Future, Market.CBOT), (time =>
{
// Monthly contracts
// Trading terminates on the last business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CBOT, Futures.Financials.MicroY2TreasuryBond, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
return lastBusinessDay;
})
},
// Micro 5-Year Yield Futures (5YY): https://www.cmegroup.com/markets/interest-rates/us-treasury/micro-5-year-yield.contractSpecs.html
{Symbol.Create(Futures.Financials.MicroY5TreasuryBond, SecurityType.Future, Market.CBOT), (time =>
{
// Monthly contracts
// Trading terminates on the last business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CBOT, Futures.Financials.MicroY5TreasuryBond, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
return lastBusinessDay;
})
},
// Micro EUR/USD Futures (M6E): https://www.cmegroup.com/markets/fx/g10/e-micro-euro.contractSpecs.html
{Symbol.Create(Futures.Currencies.MicroEUR, SecurityType.Future, Market.CME), (time =>
{
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
{
time = time.AddMonths(1);
}
// Trading terminates at 9:16 a.m. CT 2 business day prior to the 3rd Wednesday of the contract quqrter.
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.MicroEUR, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(secondBusinessDayPrecedingThirdWednesday))
{
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(secondBusinessDayPrecedingThirdWednesday, -1);
}
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14, 16, 0));
})
},
// Micro AUD/USD Futures (M6A): https://www.cmegroup.com/markets/fx/g10/e-micro-australian-dollar.contractSpecs.html
{Symbol.Create(Futures.Currencies.MicroAUD, SecurityType.Future, Market.CME), (time =>
{
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
{
time = time.AddMonths(1);
}
// On the second business day immediately preceding the third Wednesday of the contract month (usually Monday).
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.MicroAUD, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(secondBusinessDayPrecedingThirdWednesday))
{
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(secondBusinessDayPrecedingThirdWednesday, -1);
}
return secondBusinessDayPrecedingThirdWednesday;
})
},
// Micro GBP/USD Futures (M6B): https://www.cmegroup.com/markets/fx/g10/e-micro-british-pound.contractSpecs.html
{Symbol.Create(Futures.Currencies.MicroGBP, SecurityType.Future, Market.CME), (time =>
{
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
{
time = time.AddMonths(1);
}
// 9:16 a.m. Central Time (CT) on the second business day immediately preceding the third Wednesday of the contract month (usually Monday).
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.MicroGBP, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(secondBusinessDayPrecedingThirdWednesday))
{
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(secondBusinessDayPrecedingThirdWednesday, -1);
}
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14, 16, 0));
})
},
// Micro CAD/USD Futures (MCD): https://www.cmegroup.com/markets/fx/g10/e-micro-canadian-dollar-us-dollar.contractSpecs.html
{Symbol.Create(Futures.Currencies.MicroCADUSD, SecurityType.Future, Market.CME), (time =>
{
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
{
time = time.AddMonths(1);
}
// Trading terminates 1 business day prior to the 3rd Wednesday of the contract quarter.
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
var firstBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -1);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.MicroCADUSD, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(firstBusinessDayPrecedingThirdWednesday))
{
firstBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(firstBusinessDayPrecedingThirdWednesday, -1);
}
return firstBusinessDayPrecedingThirdWednesday;
})
},
// Micro JPY/USD Futures (MJY): https://www.cmegroup.com/markets/fx/g10/e-micro-japanese-yen-us-dollar.contractSpecs.html
{Symbol.Create(Futures.Currencies.MicroJPY, SecurityType.Future, Market.CME), (time =>
{
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
{
time = time.AddMonths(1);
}
// 9:16 a.m. Central Time (CT) on the second business day immediately preceding the third Wednesday of the contract month (usually Monday).
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.MicroJPY, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(secondBusinessDayPrecedingThirdWednesday))
{
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(secondBusinessDayPrecedingThirdWednesday, -1);
}
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14, 16, 0));
})
},
// Micro CHF/USD Futures (MSF): https://www.cmegroup.com/markets/fx/g10/e-micro-swiss-franc-us-dollar.contractSpecs.html
{Symbol.Create(Futures.Currencies.MicroCHF, SecurityType.Future, Market.CME), (time =>
{
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
{
time = time.AddMonths(1);
}
// 9:16 a.m. Central Time (CT) on the second business day immediately preceding the third Wednesday of the contract month (usually Monday).
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.MicroCHF, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(secondBusinessDayPrecedingThirdWednesday))
{
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(secondBusinessDayPrecedingThirdWednesday, -1);
}
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14, 16, 0));
})
},
// Micro USD/JPY Futures (M6J): https://www.cmegroup.com/markets/fx/g10/micro-usd-jpy.contractSpecs.html
{Symbol.Create(Futures.Currencies.MicroUSDJPY, SecurityType.Future, Market.CME), (time =>
{
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
{
time = time.AddMonths(1);
}
// 9:16 a.m. Central Time (CT) on the second business day immediately preceding the third Wednesday of the contract month (usually Monday).
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.MicroUSDJPY, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(secondBusinessDayPrecedingThirdWednesday))
{
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(secondBusinessDayPrecedingThirdWednesday, -1);
}
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14, 16, 0));
})
},
// Micro INR/USD Futures (MIR): https://www.cmegroup.com/markets/fx/g10/e-micro-indian-rupee.contractSpecs.html
{Symbol.Create(Futures.Currencies.MicroINRUSD, SecurityType.Future, Market.CME), (time =>
{
// Monthly contracts listed for 12 consecutive months.
// Trading terminates at 12:00 noon Mumbai time two Indian business days immediately preceding the last Indian
// business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.MicroINRUSD, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
var secondBusinessDayPrecedingLastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay,-2);
return secondBusinessDayPrecedingLastBusinessDay.Add(new TimeSpan(6,30,0));
})
},
// Micro USD/CAD Futures (M6C): https://www.cmegroup.com/markets/fx/g10/micro-usd-cad.contractSpecs.html
{Symbol.Create(Futures.Currencies.MicroCAD, SecurityType.Future, Market.CME), (time =>
{
// Two months in the March quarterly cycle (Mar, Jun, Sep, Dec)
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
{
time = time.AddMonths(1);
}
// Trading terminates at 9:16 a.m. CT, 1 business day prior to the third Wednesday of the contract month.
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
var firstBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -1);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.MicroCAD, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(firstBusinessDayPrecedingThirdWednesday))
{
firstBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(firstBusinessDayPrecedingThirdWednesday, -1);
}
return firstBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14, 16, 0));
})
},
// Micro USD/CHF Futures (M6S): https://www.cmegroup.com/markets/fx/g10/micro-usd-chf.contractSpecs.html
{Symbol.Create(Futures.Currencies.MicroUSDCHF, SecurityType.Future, Market.CME), (time =>
{
// Two months in the March quarterly cycle (Mar, Jun, Sep, Dec)
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
{
time = time.AddMonths(1);
}
// Trading terminates at 9:16 a.m. CT, 2 business days prior to the third Wednesday of the contract month.
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.MicroUSDCHF, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(secondBusinessDayPrecedingThirdWednesday))
{
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(secondBusinessDayPrecedingThirdWednesday, -1);
}
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14, 16, 0));
})
},
// Micro USD/CNH Futures (MNH): https://www.cmegroup.com/markets/fx/g10/e-micro-cnh.contractSpecs.html
{Symbol.Create(Futures.Currencies.MicroUSDCNH, SecurityType.Future, Market.CME), (time =>
{
// Monthly contracts listed for 12 consecutive months.
// Trading terminates at 11:00 a.m. Hong Kong time on the second Hong Kong business day prior
// to the third Wednesday of the contract month.
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday,-2);
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(3,0,0));
})
},
// Micro E-mini S&P 500 Index Futures (MES): https://www.cmegroup.com/markets/equities/sp/micro-e-mini-sandp-500.contractSpecs.html
{Symbol.Create(Futures.Indices.MicroSP500EMini, SecurityType.Future, Market.CME), (time =>
{
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 5 consecutive quarters
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
{
time = time.AddMonths(1);
}
// Trading terminates at 9:30 a.m. ET on the 3rd Friday of the contract month.
var thirdFriday = FuturesExpiryUtilityFunctions.ThirdFriday(time);
return thirdFriday.Add(new TimeSpan(13,30,0));
})
},
// Micro E-mini Nasdaq-100 Index Futures (MNQ): https://www.cmegroup.com/markets/equities/nasdaq/micro-e-mini-nasdaq-100.contractSpecs.html
{Symbol.Create(Futures.Indices.MicroNASDAQ100EMini, SecurityType.Future, Market.CME), (time =>
{
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 5 consecutive quarters
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
{
time = time.AddMonths(1);
}
// Trading terminates at 9:30 a.m. ET on the 3rd Friday of the contract month.
var thirdFriday = FuturesExpiryUtilityFunctions.ThirdFriday(time);
return thirdFriday.Add(new TimeSpan(13,30,0));
})
},
// Micro E-mini Russell 2000 Index Futures (M2K): https://www.cmegroup.com/markets/equities/russell/micro-e-mini-russell-2000.contractSpecs.html
{Symbol.Create(Futures.Indices.MicroRussell2000EMini, SecurityType.Future, Market.CME), (time =>
{
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 5 consecutive quarters
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
{
time = time.AddMonths(1);
}
// Trading terminates at 9:30 a.m. ET on the 3rd Friday of the contract month.
var thirdFriday = FuturesExpiryUtilityFunctions.ThirdFriday(time);
return thirdFriday.Add(new TimeSpan(13,30,0));
})
},
// Micro E-mini Dow Jones Industrial Average Index Futures (MYM): https://www.cmegroup.com/markets/equities/dow-jones/micro-e-mini-dow.contractSpecs.html
{Symbol.Create(Futures.Indices.MicroDow30EMini, SecurityType.Future, Market.CBOT), (time =>
{
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 4 consecutive quarters
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
{
time = time.AddMonths(1);
}
// Trading can occur up to 9:30 a.m. Eastern Time (ET) on the 3rd Friday of the contract month
var thirdFriday = FuturesExpiryUtilityFunctions.ThirdFriday(time);
return thirdFriday.Add(new TimeSpan(13,30,0));
})
},
// Micro WTI Crude Oil Futures (MCL): https://www.cmegroup.com/markets/energy/crude-oil/micro-wti-crude-oil.contractSpecs.html
{Symbol.Create(Futures.Energies.MicroCrudeOilWTI, SecurityType.Future, Market.NYMEX), (time =>
{
// Monthly contracts listed for 12 consecutive months and additional Jun and Dec contract months
// Trading terminates 4 business days prior to the 25th calendar day of the month prior to the
// contract month (1 business day prior to CL LTD)
var previousMonth = time.AddMonths(-1);
var twentyFifthDay = new DateTime(previousMonth.Year, previousMonth.Month, 25);
var twentyFifthDayLessFour = FuturesExpiryUtilityFunctions.AddBusinessDays(twentyFifthDay, -4);
return twentyFifthDayLessFour;
})
},
// Micro Singapore FOB Marine Fuel 0.5% (Platts) Futures (S50): https://www.cmegroup.com/markets/energy/refined-products/micro-singapore-fob-marine-fuel-05-platts.contractSpecs.html
{Symbol.Create(Futures.Energies.MicroSingaporeFOBMarineFuelZeroPointFivePercetPlatts, SecurityType.Future, Market.NYMEX), (time =>
{
// Monthly contracts listed for the current year and next 3 calendar years
// Add monthly contracts for a new calendar year following the termination of trading in the
// December contract of the current year.
// Trading terminates on the last business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.NYMEX, Futures.Energies.MicroSingaporeFOBMarineFuelZeroPointFivePercetPlatts, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
return lastBusinessDay;
})
},
// Micro Gasoil 0.1% Barges FOB ARA (Platts) Futures (M1B): https://www.cmegroup.com/markets/energy/refined-products/micro-gasoil-01-barges-fob-rdam-platts.contractSpecs.html
{Symbol.Create(Futures.Energies.MicroGasoilZeroPointOnePercentBargesFOBARAPlatts, SecurityType.Future, Market.NYMEX), (time =>
{
// Monthly contracts listed for 36 consecutive months
// Trading terminates on the last London business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.NYMEX, Futures.Energies.MicroGasoilZeroPointOnePercentBargesFOBARAPlatts, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
return lastBusinessDay;
})
},
// Micro European FOB Rdam Marine Fuel 0.5% Barges (Platts) Futures (R50): https://www.cmegroup.com/markets/energy/refined-products/micro-european-fob-rdam-marine-fuel-05-barges-platts.contractSpecs.html
{Symbol.Create(Futures.Energies.MicroEuropeanFOBRdamMarineFuelZeroPointFivePercentBargesPlatts, SecurityType.Future, Market.NYMEX), (time =>
{
// Monthly contracts listed for the current year and next 3 calendar years.
// Add monthly contracts for a new calendar year following the termination of trading
// in the December contract of the current year.
// Trading terminates on the last London business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.NYMEX, Futures.Energies.MicroEuropeanFOBRdamMarineFuelZeroPointFivePercentBargesPlatts, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
return lastBusinessDay;
})
},
// Micro European 3.5% Fuel Oil Barges FOB Rdam (Platts) Futures (MEF): https://www.cmegroup.com/markets/energy/refined-products/micro-european-35-fuel-oil-barges-fob-rdam-platts.contractSpecs.html
{Symbol.Create(Futures.Energies.MicroEuropeanThreePointFivePercentOilBargesFOBRdamPlatts, SecurityType.Future, Market.NYMEX), (time =>
{
// Monthly contracts listed for the current year and 5 calendar years.Monthly contracts for a new calendar
// year will be added following the termination of trading in the December contract of the current year.
// Trading terminates on the last business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.NYMEX, Futures.Energies.MicroEuropeanThreePointFivePercentOilBargesFOBRdamPlatts, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
return lastBusinessDay;
})
},
// Micro Singapore Fuel Oil 380CST (Platts) Futures (MAF): https://www.cmegroup.com/markets/energy/refined-products/micro-singapore-fuel-oil-380cst-platts.contractSpecs.html
{Symbol.Create(Futures.Energies.MicroSingaporeFuelOil380CSTPlatts, SecurityType.Future, Market.NYMEX), (time =>
{
// Monthly contracts listed for the current year and 5 calendar years.Monthly contracts for a new calendar
// year will be added following the termination of trading in the December contract of the current year.
// Trading terminates on the last business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.NYMEX, Futures.Energies.MicroSingaporeFuelOil380CSTPlatts, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
return lastBusinessDay;
})
},
// Micro Coal (API 5) fob Newcastle (Argus/McCloskey) Futures (M5F): https://www.cmegroup.com/markets/energy/coal/micro-coal-api-5-fob-newcastle-argus-mccloskey.contractSpecs.html
{Symbol.Create(Futures.Energies.MicroCoalAPIFivefobNewcastleArgusMcCloskey, SecurityType.Future, Market.NYMEX), (time =>
{
// Monthly contracts listed for the current year and the next calendar year. Monthly contracts
// for a new calendar year will be added following the termination of trading in the December
// contract of the current year.
// Trading terminates on the last Friday of the contract month. If such Friday is a UK holiday,
// trading terminates on the UK business day immediately prior to the last Friday of the contract
// month unless such day is not an Exchange business day, in which case trading terminates on the
// Exchange business day immediately prior.
var lastFriday = FuturesExpiryUtilityFunctions.LastFriday(time);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.NYMEX, Futures.Energies.MicroCoalAPIFivefobNewcastleArgusMcCloskey, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastFriday))
{
lastFriday = FuturesExpiryUtilityFunctions.AddBusinessDays(lastFriday, -1);
while (holidays.Contains(lastFriday))
{
lastFriday = FuturesExpiryUtilityFunctions.AddBusinessDays(lastFriday, -1);
}
}
return lastFriday;
})
},
// Micro European 3.5% Fuel Oil Cargoes FOB Med (Platts) Futures (M35): https://www.cmegroup.com/markets/energy/refined-products/micro-european-35-fuel-oil-cargoes-fob-med-platts.contractSpecs.html
{Symbol.Create(Futures.Energies.MicroEuropeanThreePointFivePercentFuelOilCargoesFOBMedPlatts, SecurityType.Future, Market.NYMEX), (time =>
{
// Monthly contracts listed for 36 consecutive months
// Trading terminates on the last business day of the contract month.
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.NYMEX, Futures.Energies.MicroEuropeanThreePointFivePercentFuelOilCargoesFOBMedPlatts, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastBusinessDay))
{
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
}
return lastBusinessDay;
})
},
// Micro Ether Futures (MET): https://www.cmegroup.com/markets/cryptocurrencies/ether/micro-ether.contractSpecs.html
{Symbol.Create(Futures.Currencies.MicroEther, SecurityType.Future, Market.CME), (time =>
{
// Monthly contracts listed for 6 consecutive months and 2 additional Dec contract months.
// Trading terminates at 4:00 p.m. London time on the last Friday of the contract month that
// is either a London or U.S. business day. If the last Friday of the contract month day is
// not a business day in both London and the U.S., trading terminates on the prior London or
// U.S. business day.
// BTIC: Trading terminates at 4:00 p.m. London time on the last Thursday of the contract month
// that is either a London or U.S. business day. If the last Thursday of the contract month day
// is not a business day in both London and the U.S., trading terminates on the prior London or U.S.
// business day.
var lastFriday = FuturesExpiryUtilityFunctions.LastFriday(time);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.MicroEther, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastFriday))
{
lastFriday = FuturesExpiryUtilityFunctions.AddBusinessDays(lastFriday, -1);
}
return lastFriday.Add(new TimeSpan(15, 0, 0));
})
},
// Micro Bitcoin Futures (MBT): https://www.cmegroup.com/markets/cryptocurrencies/bitcoin/micro-bitcoin.contractSpecs.html
{Symbol.Create(Futures.Currencies.MicroBTC, SecurityType.Future, Market.CME), (time =>
{
// Monthly contracts listed for 6 consecutive months and 2 additional Dec contract months.
// If the 6 consecutive months includes Dec, list only 1 additional Dec contract month.
// Trading terminates at 4:00 p.m. London time on the last Friday of the contract month.
// If this is not both a London and U.S. business day, trading terminates on the prior
// London and the U.S. business day.
// BTIC: Trading terminates at 4:00 p.m. London time on the last Thursday of the contract
// month.If this is not both a London and U.S. business day, trading terminates on the prior
// London and the U.S. business day.
var lastFriday = FuturesExpiryUtilityFunctions.LastFriday(time);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.MicroBTC, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastFriday))
{
lastFriday = FuturesExpiryUtilityFunctions.AddBusinessDays(lastFriday, -1);
}
return lastFriday.Add(new TimeSpan(15, 0, 0));
})
},
// BTIC on Micro Ether Futures (MRB): https://www.cmegroup.com/markets/cryptocurrencies/ether/micro-ether.contractSpecs.html
{Symbol.Create(Futures.Currencies.BTICMicroEther, SecurityType.Future, Market.CME), (time =>
{
// Monthly contracts listed for 6 consecutive months and 2 additional Dec contract months.
// Trading terminates at 4:00 p.m. London time on the last Friday of the contract month.
// If this is not both a London and U.S. business day, trading terminates on the prior
// London and the U.S. business day.
// BTIC: Trading terminates at 4:00 p.m. London time on the last Thursday of the contract
// month.If this is not both a London and U.S. business day, trading terminates on the prior
// London and the U.S. business day.
var lastThursday = FuturesExpiryUtilityFunctions.LastThursday(time);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.BTICMicroEther, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastThursday))
{
lastThursday = FuturesExpiryUtilityFunctions.AddBusinessDays(lastThursday, -1);
}
return lastThursday.Add(new TimeSpan(15, 0, 0));
})
},
// BTIC on Micro Bitcoin Futures (MIB): https://www.cmegroup.com/markets/cryptocurrencies/bitcoin/micro-bitcoin.contractSpecs.html
{Symbol.Create(Futures.Currencies.BTICMicroBTC, SecurityType.Future, Market.CME), (time =>
{
// Monthly contracts listed for 6 consecutive months and 2 additional Dec contract months.
// If the 6 consecutive months includes Dec, list only 1 additional Dec contract month.
// Trading terminates at 4:00 p.m. London time on the last Friday of the contract month.
// If this is not both a London and U.S. business day, trading terminates on the prior
// London and the U.S. business day.
// BTIC: Trading terminates at 4:00 p.m. London time on the last Thursday of the contract
// month.If this is not both a London and U.S. business day, trading terminates on the prior
// London and the U.S. business day.
var lastThursday = FuturesExpiryUtilityFunctions.LastThursday(time);
var holidays = MarketHoursDatabase.FromDataFolder()
.GetEntry(Market.CME, Futures.Currencies.BTICMicroBTC, SecurityType.Future)
.ExchangeHours
.Holidays;
while (holidays.Contains(lastThursday))
{
lastThursday = FuturesExpiryUtilityFunctions.AddBusinessDays(lastThursday, -1);
}
return lastThursday.Add(new TimeSpan(15, 0, 0));
})
}
};
}
}

View File

@@ -1,4 +1,4 @@
/*
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
@@ -257,12 +257,19 @@ namespace QuantConnect.Securities.Future
}
/// <summary>
/// Method to retrieve the last weekday of any month
/// Method to retrieve the last Thursday of any month
/// </summary>
/// <param name="time">Date from the given month</param>
/// <returns>Last day of the we</returns>
/// <returns>Last Thursday of the given month</returns>
public static DateTime LastThursday(DateTime time) => LastWeekday(time, DayOfWeek.Thursday);
/// <summary>
/// Method to retrieve the last Friday of any month
/// </summary>
/// <param name="time">Date from the given month</param>
/// <returns>Last Friday of the given month</returns>
public static DateTime LastFriday(DateTime time) => LastWeekday(time, DayOfWeek.Friday);
/// <summary>
/// Method to check whether a given time is holiday or not
/// </summary>

View File

@@ -0,0 +1,105 @@
/*
* 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.Globalization;
using QuantConnect.Logging;
namespace QuantConnect.Securities.Future
{
/// <summary>
/// POCO class for modeling margin requirements at given date
/// </summary>
public class MarginRequirementsEntry
{
/// <summary>
/// Date of margin requirements change
/// </summary>
public DateTime Date { get; init; }
/// <summary>
/// Initial overnight margin for the contract effective from the date of change
/// </summary>
public decimal InitialOvernight { get; init; }
/// <summary>
/// Maintenance overnight margin for the contract effective from the date of change
/// </summary>
public decimal MaintenanceOvernight { get; init; }
/// <summary>
/// Initial intraday margin for the contract effective from the date of change
/// </summary>
public decimal InitialIntraday { get; init; }
/// <summary>
/// Maintenance intraday margin for the contract effective from the date of change
/// </summary>
public decimal MaintenanceIntraday { get; init; }
/// <summary>
/// Creates a new instance of <see cref="MarginRequirementsEntry"/> from the specified csv line
/// </summary>
/// <param name="csvLine">The csv line to be parsed</param>
/// <returns>A new <see cref="MarginRequirementsEntry"/> for the specified csv line</returns>
public static MarginRequirementsEntry Create(string csvLine)
{
var line = csvLine.Split(',');
DateTime date;
if (!DateTime.TryParseExact(line[0], DateFormat.EightCharacter, CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
{
Log.Trace($"Couldn't parse date/time while reading future margin requirement file. Line: {csvLine}");
}
decimal initialOvernight;
if (!decimal.TryParse(line[1], out initialOvernight))
{
Log.Trace($"Couldn't parse Initial Overnight margin requirements while reading future margin requirement file. Line: {csvLine}");
}
decimal maintenanceOvernight;
if (!decimal.TryParse(line[2], out maintenanceOvernight))
{
Log.Trace($"Couldn't parse Maintenance Overnight margin requirements while reading future margin requirement file. Line: {csvLine}");
}
// default value, if present in file we try to parse
decimal initialIntraday = initialOvernight * 0.4m;
if (line.Length >= 4
&& !decimal.TryParse(line[3], out initialIntraday))
{
Log.Trace($"Couldn't parse Initial Intraday margin requirements while reading future margin requirement file. Line: {csvLine}");
}
// default value, if present in file we try to parse
decimal maintenanceIntraday = maintenanceOvernight * 0.4m;
if (line.Length >= 5
&& !decimal.TryParse(line[4], out maintenanceIntraday))
{
Log.Trace($"Couldn't parse Maintenance Intraday margin requirements while reading future margin requirement file. Line: {csvLine}");
}
return new MarginRequirementsEntry
{
Date = date,
InitialOvernight = initialOvernight,
MaintenanceOvernight = maintenanceOvernight,
InitialIntraday = initialIntraday,
MaintenanceIntraday = maintenanceIntraday
};
}
}
}

View File

@@ -29,7 +29,7 @@ namespace QuantConnect.Securities.Option.StrategyMatcher
/// <summary>
/// Gets a new <see cref="OptionPosition"/> with zero <see cref="Quantity"/>
/// </summary>
public static OptionPosition None(Symbol symbol)
public static OptionPosition Empty(Symbol symbol)
=> new OptionPosition(symbol, 0);
/// <summary>

View File

@@ -247,14 +247,6 @@ namespace QuantConnect.Securities
/// <param name="dataType">The data type</param>
public void StoreData(IReadOnlyList<BaseData> data, Type dataType)
{
#if DEBUG // don't run this in release as we should never fail here, but it's also nice to have here as documentation of intent
if (data.DistinctBy(d => d.GetType()).Skip(1).Any())
{
throw new ArgumentException(
"SecurityCache.StoreData data list must contain elements of the same type."
);
}
#endif
if (dataType == typeof(Tick))
{
var tick = data[data.Count - 1] as Tick;

View File

@@ -1,4 +1,4 @@
/*
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
@@ -124,7 +124,8 @@ namespace QuantConnect.Storage
{
encoding = encoding ?? Encoding.UTF8;
return encoding.GetString(_store.ReadBytes(key));
var data = _store.ReadBytes(key);
return data != null ? encoding.GetString(data) : null;
}
/// <summary>
@@ -172,17 +173,35 @@ namespace QuantConnect.Storage
}
}
/// <summary>
/// Saves the data from a local file path associated with the specified key
/// </summary>
/// <remarks>If the file does not exist it will throw an exception</remarks>
/// <param name="key">The object key</param>
/// <returns>True if the object was saved successfully</returns>
public bool Save(string key)
{
// Check the file exists
var filePath = GetFilePath(key);
if (!File.Exists(filePath))
{
throw new ArgumentException($"There is no file associated with key {key} in '{filePath}'");
}
var bytes = File.ReadAllBytes(filePath);
return _store.SaveBytes(key, bytes);
}
/// <summary>
/// Saves the object data in text format for the specified key
/// </summary>
/// <param name="key">The object key</param>
/// <param name="text">The string object to be saved</param>
/// <param name="encoding">The string encoding used</param>
/// <param name="encoding">The string encoding used, <see cref="Encoding.UTF8"/> by default</param>
/// <returns>True if the object was saved successfully</returns>
public bool Save(string key, string text, Encoding encoding = null)
{
encoding = encoding ?? Encoding.UTF8;
encoding ??= Encoding.UTF8;
return _store.SaveBytes(key, encoding.GetBytes(text));
}

View File

@@ -15,7 +15,6 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using NodaTime;
using QuantConnect.Logging;
@@ -487,7 +486,7 @@ namespace QuantConnect
/// </summary>
/// <remarks>
/// This is mainly used to bridge the gap between exchange time zone and data time zone for file written to disk. The returned
/// enumerable of dates is gauranteed to be the same size or longer than those generated via <see cref="EachTradeableDay(ICollection{Security},DateTime,DateTime)"/>
/// enumerable of dates is guaranteed to be the same size or longer than those generated via <see cref="EachTradeableDay(ICollection{Security},DateTime,DateTime)"/>
/// </remarks>
/// <param name="exchange">The exchange hours</param>
/// <param name="from">The start time in the exchange time zone</param>

View File

@@ -16,8 +16,8 @@
using System;
using QuantConnect.Securities;
using QuantConnect.Securities.Cfd;
using QuantConnect.Securities.Crypto;
using QuantConnect.Securities.Forex;
using QuantConnect.Securities.Crypto;
namespace QuantConnect.Util
{
@@ -29,28 +29,48 @@ namespace QuantConnect.Util
private static readonly Lazy<SymbolPropertiesDatabase> SymbolPropertiesDatabase =
new Lazy<SymbolPropertiesDatabase>(Securities.SymbolPropertiesDatabase.FromDataFolder);
/// <summary>
/// Tries to decomposes the specified currency pair into a base and quote currency provided as out parameters
/// </summary>
/// <param name="currencyPair">The input currency pair to be decomposed</param>
/// <param name="baseCurrency">The output base currency</param>
/// <param name="quoteCurrency">The output quote currency</param>
/// <returns>True if was able to decompose the currency pair</returns>
public static bool TryDecomposeCurrencyPair(Symbol currencyPair, out string baseCurrency, out string quoteCurrency)
{
baseCurrency = null;
quoteCurrency = null;
if (!IsValidSecurityType(currencyPair?.SecurityType, throwException: false))
{
return false;
}
try
{
DecomposeCurrencyPair(currencyPair, out baseCurrency, out quoteCurrency);
return true;
}
catch
{
// ignored
}
return false;
}
/// <summary>
/// Decomposes the specified currency pair into a base and quote currency provided as out parameters
/// </summary>
/// <param name="currencyPair">The input currency pair to be decomposed</param>
/// <param name="baseCurrency">The output base currency</param>
/// <param name="quoteCurrency">The output quote currency</param>
public static void DecomposeCurrencyPair(Symbol currencyPair, out string baseCurrency, out string quoteCurrency)
/// <param name="defaultQuoteCurrency">Optionally can provide a default quote currency</param>
public static void DecomposeCurrencyPair(Symbol currencyPair, out string baseCurrency, out string quoteCurrency, string defaultQuoteCurrency = Currencies.USD)
{
if (currencyPair == null)
{
throw new ArgumentException("Currency pair must not be null");
}
IsValidSecurityType(currencyPair?.SecurityType, throwException: true);
var securityType = currencyPair.SecurityType;
if (securityType != SecurityType.Forex &&
securityType != SecurityType.Cfd &&
securityType != SecurityType.Crypto)
{
throw new ArgumentException($"Unsupported security type: {securityType}");
}
if (securityType == SecurityType.Forex)
{
Forex.DecomposeCurrencyPair(currencyPair.Value, out baseCurrency, out quoteCurrency);
@@ -61,7 +81,7 @@ namespace QuantConnect.Util
currencyPair.ID.Market,
currencyPair,
currencyPair.SecurityType,
Currencies.USD);
defaultQuoteCurrency);
if (securityType == SecurityType.Cfd)
{
@@ -184,5 +204,30 @@ namespace QuantConnect.Util
return Match.NoMatch;
}
private static bool IsValidSecurityType(SecurityType? securityType, bool throwException)
{
if (securityType == null)
{
if (throwException)
{
throw new ArgumentException("Currency pair must not be null");
}
return false;
}
if (securityType != SecurityType.Forex &&
securityType != SecurityType.Cfd &&
securityType != SecurityType.Crypto)
{
if (throwException)
{
throw new ArgumentException($"Unsupported security type: {securityType}");
}
return false;
}
return true;
}
}
}

View File

@@ -35,7 +35,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SharpZipLib" Version="1.2.0" />
<PackageReference Include="SharpZipLib" Version="1.3.3" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />

Binary file not shown.

View File

@@ -0,0 +1,4 @@
date,initial,maintenance
20210816,193,175
20211130,220,200
20220204,264,240
1 date initial maintenance
2 20210816 193 175
3 20211130 220 200
4 20220204 264 240

View File

@@ -0,0 +1,5 @@
date,initial,maintenance
20210816,132,120
20211112,143,130
20211130,176,160
20220204,209,190
1 date initial maintenance
2 20210816 132 120
3 20211112 143 130
4 20211130 176 160
5 20220204 209 190

View File

@@ -0,0 +1,3 @@
date,initial,maintenance
20210816,220,200
20220204,264,240
1 date initial maintenance
2 20210816 220 200
3 20220204 264 240

View File

@@ -0,0 +1,5 @@
date,initial,maintenance
20210816,154,140
20211112,182,165
20211130,220,200
20220204,264,240
1 date initial maintenance
2 20210816 154 140
3 20211112 182 165
4 20211130 220 200
5 20220204 264 240

View File

@@ -19,4 +19,7 @@ date,initial,maintenance
20200316,363,330
20200331,495,450
20200424,550,500
20200526,462,420
20200526,462,420
20200605,396,360
20201211,341,310
20211130,413,375
1 date initial maintenance
19 20200316 363 330
20 20200331 495 450
21 20200424 550 500
22 20200526 462 420
23 20200605 396 360
24 20201211 341 310
25 20211130 413 375

View File

@@ -6,4 +6,9 @@ date,initial,maintenance
20190308,275,250
20190503,242,220
20190726,330,300
20190823,385,350
20190823,385,350
20201030,440,400
20210108,583,530
20210114,660,600
20210625,798,725
20210709,880,800
1 date initial maintenance
6 20190308 275 250
7 20190503 242 220
8 20190726 330 300
9 20190823 385 350
10 20201030 440 400
11 20210108 583 530
12 20210114 660 600
13 20210625 798 725
14 20210709 880 800

View File

@@ -6,4 +6,15 @@ date,initial,maintenance
20190308,374,340
20190503,330,300
20190726,363,330
20200422,402,365
20200422,402,365
20200731,385,350
20201002,429,390
20201215,495,450
20210108,550,500
20210114,660,600
20210625,726,660
20210709,792,720
20210730,825,750
20210922,880,800
20220107,913,830
20220128,1045,950
1 date initial maintenance
6 20190308 374 340
7 20190503 330 300
8 20190726 363 330
9 20200422 402 365
10 20200731 385 350
11 20201002 429 390
12 20201215 495 450
13 20210108 550 500
14 20210114 660 600
15 20210625 726 660
16 20210709 792 720
17 20210730 825 750
18 20210922 880 800
19 20220107 913 830
20 20220128 1045 950

View File

@@ -33,4 +33,11 @@ date,initial,maintenance
20190815,3025,2750
20200117,2365,2150
20200318,3190,2900
20200325,4785,4350
20200325,4785,4350
20200616,3850,3500
20200903,3300,3000
20201016,2970,2700
20210129,3300,3000
20210405,3850,3500
20210423,4290,3900
20210507,4730,4300
1 date initial maintenance
33 20190815 3025 2750
34 20200117 2365 2150
35 20200318 3190 2900
36 20200325 4785 4350
37 20200616 3850 3500
38 20200903 3300 3000
39 20201016 2970 2700
40 20210129 3300 3000
41 20210405 3850 3500
42 20210423 4290 3900
43 20210507 4730 4300

View File

@@ -22,4 +22,14 @@ date,initial,maintenance
20200117,1073,975
20200219,913,830
20200303,1056,960
20200312,1265,1150
20200312,1265,1150
20200721,1045,950
20200925,825,750
20201120,715,650
20210108,649,590
20210301,770,700
20210319,935,850
20210604,836,760
20210917,770,700
20211112,902,820
20211130,1122,1020
1 date initial maintenance
22 20200117 1073 975
23 20200219 913 830
24 20200303 1056 960
25 20200312 1265 1150
26 20200721 1045 950
27 20200925 825 750
28 20201120 715 650
29 20210108 649 590
30 20210301 770 700
31 20210319 935 850
32 20210604 836 760
33 20210917 770 700
34 20211112 902 820
35 20211130 1122 1020

View File

@@ -28,4 +28,18 @@ date,initial,maintenance
20190314,1485,1350
20190603,1595,1450
20191113,1375,1250
20200320,1595,1450
20200320,1595,1450
20200617,1375,1250
20200724,1265,1150
20200814,1155,1050
20201002,1375,1250
20201117,1485,1350
20210104,1650,1500
20210106,1760,1600
20210114,1925,1750
20210205,2035,1850
20210426,2255,2050
20210429,2475,2250
20210507,2585,2350
20210917,2338,2125
20220127,2695,2450
1 date initial maintenance
28 20190314 1485 1350
29 20190603 1595 1450
30 20191113 1375 1250
31 20200320 1595 1450
32 20200617 1375 1250
33 20200724 1265 1150
34 20200814 1155 1050
35 20201002 1375 1250
36 20201117 1485 1350
37 20210104 1650 1500
38 20210106 1760 1600
39 20210114 1925 1750
40 20210205 2035 1850
41 20210426 2255 2050
42 20210429 2475 2250
43 20210507 2585 2350
44 20210917 2338 2125
45 20220127 2695 2450

View File

@@ -0,0 +1,16 @@
date,initial,maintenance
20190503,649,590
20190523,605,550
20191101,550,500
20200109,605,550
20200302,682,620
20200304,748,680
20200310,825,750
20200312,908,825
20200316,1067,970
20200318,1320,1200
20200526,1177,1070
20200804,1045,950
20201211,990,900
20211210,935,850
20220121,880,800
1 date initial maintenance
2 20190503 649 590
3 20190523 605 550
4 20191101 550 500
5 20200109 605 550
6 20200302 682 620
7 20200304 748 680
8 20200310 825 750
9 20200312 908 825
10 20200316 1067 970
11 20200318 1320 1200
12 20200526 1177 1070
13 20200804 1045 950
14 20201211 990 900
15 20211210 935 850
16 20220121 880 800

View File

@@ -24,4 +24,13 @@ date,initial,maintenance
20200311,2970,2700
20200313,3410,3100
20200319,3905,3550
20200526,3520,3200
20200526,3520,3200
20200626,3300,3000
20200707,2970,2700
20200721,2750,2500
20201120,2530,2300
20210108,2255,2050
20210226,2420,2200
20210319,2695,2450
20210910,2420,2200
20211130,2695,2450
1 date initial maintenance
24 20200311 2970 2700
25 20200313 3410 3100
26 20200319 3905 3550
27 20200526 3520 3200
28 20200626 3300 3000
29 20200707 2970 2700
30 20200721 2750 2500
31 20201120 2530 2300
32 20210108 2255 2050
33 20210226 2420 2200
34 20210319 2695 2450
35 20210910 2420 2200
36 20211130 2695 2450

View File

@@ -38,4 +38,10 @@ date,initial,maintenance
20200318,13420,12200
20200324,15400,14000
20200428,13750,12500
20200526,12650,11500
20200526,12650,11500
20200619,11660,10600
20200721,11000,10000
20201120,9570,8700
20210108,8690,7900
20210416,7920,7200
20210527,7150,6500
1 date initial maintenance
38 20200318 13420 12200
39 20200324 15400 14000
40 20200428 13750 12500
41 20200526 12650 11500
42 20200619 11660 10600
43 20200721 11000 10000
44 20201120 9570 8700
45 20210108 8690 7900
46 20210416 7920 7200
47 20210527 7150 6500

Some files were not shown because too many files have changed in this diff Show More