Compare commits

..

136 Commits

Author SHA1 Message Date
Gerardo Salazar
be6ead75e9 Revert some changes and adds pandas memory allocator 2020-12-06 19:15:38 -08:00
Gerardo Salazar
25f7ccf0c2 Performance improvements 2020-12-06 19:15:38 -08:00
Gerardo Salazar
afb0a86291 Faster double casting than CLR implementation 2020-12-06 19:14:48 -08:00
Gerardo Salazar
f5c5faa3d4 Remove bindingRedirect in Tests and re-adds pyarrow to DockerfileLeanFoundation 2020-12-06 19:14:48 -08:00
Gerardo Salazar
c471d1ced0 Adds unit tests and fixes various bugs in PandasConverter 2020-12-06 19:14:48 -08:00
Gerardo Salazar
5c345faea7 Revert changes to DockerfileLeanFoundation 2020-12-06 19:14:24 -08:00
Gerardo Salazar
11bed1f0c3 Address self-review: Adds explanitory comments and cleans up code 2020-12-06 19:14:24 -08:00
Gerardo Salazar
ec57799a5d Reverts changes and deletes unused classes 2020-12-06 19:14:24 -08:00
Gerardo Salazar
4c8aa638eb Fixes failing unit tests by converting filter mask to PyObject[] 2020-12-06 19:13:54 -08:00
Gerardo Salazar
7c3caec07f Use external NuGet package for Arrow
* Updates CI for Arrow
  * Updates DockerfileLeanFoundation
  * Adds Arrow to Python packages tests
2020-12-06 19:13:54 -08:00
Gerardo Salazar
45f71543bd Filters duplicates more efficiently, fixes warning on removal of dupes
* Commit includes behind the scenes testing and validation of the data
    and performance as a result of modifying the dupe removal process
2020-12-06 19:13:54 -08:00
Gerardo Salazar
13b9d91ecb Memory leak fixes for options and performance improvement for NA replace 2020-12-06 19:13:54 -08:00
Gerardo Salazar
f239018e77 Tames the wild beast of memory leaks, for QuoteBar data 2020-12-06 19:13:54 -08:00
Gerardo Salazar
f1c98b848a Partially fixes memory leak in PandasConverter 2020-12-06 19:13:54 -08:00
Gerardo Salazar
2c7bde422a Reuses MemoryStream to avoid having to reallocate consistently
* Cleans up .csproj and packages.config
  * Adds IDisposable to PandasConverter, PandasArrowMemoryAllocator
2020-12-06 19:13:54 -08:00
Gerardo Salazar
9e95bfea90 Fixes various bugs and matches previous version for backwards compat 2020-12-06 19:13:35 -08:00
Gerardo Salazar
b820ff4de8 EOD commit; Attempts to implement alt. data support 2020-12-06 19:13:35 -08:00
Gerardo Salazar
8218a2be99 Fixes bug with options and refactors code
* Option index is now 1-1 with existing impl
  * Begin adding support for custom data
2020-12-06 19:13:35 -08:00
Gerardo Salazar
9e3031c562 Fixes options support
* 1 bug remaining: Get index to be ''
2020-12-06 19:13:35 -08:00
Gerardo Salazar
2cf9239b6e Improves performance by ~10% of Arrow implementation
* New allocator improves performance by reusing allocated buffers
2020-12-06 19:13:35 -08:00
Gerardo Salazar
4c63c60a89 Adds option expiry support 2020-12-06 19:13:35 -08:00
Gerardo Salazar
9a2e47b05c Adds support for future expiry and proper indexing 2020-12-06 19:13:35 -08:00
Gerardo Salazar
711d46d6e2 Fixes bugs with index and handling of no data 2020-12-06 19:13:35 -08:00
Gerardo Salazar
5d6625c1ea Adds support for Tick, OpenInterest
* Performance improvement for Symbol creation in TimeSliceFactory for
    futures
2020-12-06 19:13:34 -08:00
Gerardo Salazar
0a70611d81 MVP of Apache Arrow in-memory pandas DataFrame construction 2020-12-06 19:13:16 -08:00
Gerardo Salazar
43c6d5cc5a Adds extra simplified history request method in PortfoliioLooper (#5003) 2020-12-06 15:13:32 -08:00
Martin-Molinero
f39acceadc Load fine fundamental from data cache folder (#4995)
* Load fine fundamental from data cache folder

* Add daily backwards lookup for live fine
2020-12-04 18:28:24 -08:00
Jasper van Merle
921ddced04 Fix invalid xml on SafeMultiply100 documentation (#5000) 2020-12-04 18:22:12 -08:00
Gerardo Salazar
ff8563244f Fixes Random Length Lumber contract multiplier/min price variation (#4993) 2020-12-04 12:33:19 -03:00
Colton Sellers
041f1d9db5 Limit Futures Resolutions in BaseData (#4979)
* Limit futures support resolutions

* Fix broken tests

* Address review
2020-12-03 21:50:17 -03:00
Gerardo Salazar
eb1181f5f7 Adds Futures Options Asset Class w/ IB Support (#4928)
* Adds preliminary universe selection for Future Options

* Fixes scaling issues with Future Options

* Fixes scaling multiplying by 10000x instead of using _scaleFactor

* Fixes scaling for Tick

* Revert changes to Tick since it divides the scaling factor

* Changes stale method name to new method name after rebase

* Fixes selection bugs, adds new methods, and adds unit tests

  * Fixes bug where Equity Symbol was created for an underlying
    non-equity Symbol, resulting in equity data trying to be loaded

  * Adds unit tests covering changes to Tick, QuoteBar, TradeBar and
    LeanData

  * Adds regression test for AddUniverseOption filter contract selection
    for Future Options

* Addresses review - modifies the AddFutureOption signature

  * Adds new AddUniverseOptions method overload
  * Removes and adds a new unit test
  * Misc. modifications to account for new changes

* Fixes bug where futures were loaded using default SID Date

  * Refactors and removes unnecessary work
  * Fixes regression algorithm, which previously made no trades

* Adds future option data

  * Adds the corresponding underlying data, in this case, futures data
    to enable usage of future options data

* Replaces data with new data (ES18Z20)

  * Improves Future chain filtering and updates regression stats

* Add AddFutureOptionContract API

* Expands regression and unit tests to test in finer detail

* Adds Python regression algorithms for AddFutureOption[Contract] methods

* Adds new unit test for BacktestingOptionChainProvider

  * Fixes bug with BacktesingOptionChainProvider where we
    attempted to load the Trades option chain first, resulting
    in breakage of backwards compatibility and limitation of the
    option chain.

  * Adds new regression algorithms (Py) to Algorithm.Python project

* Adds FutureOptionMarginBuyingPowerModel

  * Modifies code paths used to select margin model
  * Adds related unit tests for margin model

* Fixes issue with unit test and MHDB/SPDB lookup for Future Options

* Preliminary regression algorithm testing ITM call/put option buying

  * Fixes bug where fee model used did not find non-US market
    options fee model. We now use the futures fee model for future
    options because IB charges the same commissions per contract
    between futures and futures options

* Adds proper regression algorithm for ITM future options expiration

* Pushing broken algorithm for review

  * Currently, algorithm does not fill forward, causing
    a single future option to not get exercised when it is delisted.

* Adds FutureOptionPutITMExpiryRegressionAlgorithm

  * Improves existing regression algorithm for call side
  * Fixes bug in existing regression algorithm
  * Adds AAPL daily data to advance enumerator for ^^^ fix

* Adds additional future option regression algorithms

  * Adds Buy OTM expiration regression algorithms
  * Adds Sell ITM/OTM expiration regression algorithms
  * Adds missing Python regression algorithms

* Adds remaining Python regression algorithms and fixes issues

  * Fixes naming issues and statistics
  * Adds short option OTM regression algorithms (Py)

* Add license header and class comments to python algorithms

  * Cleans up comments and docstrings
  * Create Buy/Sell call intraday regression algo

* Redirects future options symbol properties to futures symbol properties

  * Asserts exercise/assignment price and updates stats in regression algos
  * Adds new unit test covering changes to SecurityService

* Adds comments and fixes failing test

* Partially fixes future option mis-calculated profit/loss

* Adjusts portfolio model to calculate FOP as a no upfront pay asset class

  * Updates regression algorithm statistics

* Begin IB FOP support

* Initial support for FOP IB data streaming, live í¾‰

  * Adds additional functionality to LiveOptionChainProvider
    - Allows querying CME API to retrieve option chains for CME products
    - Ultimately, it's also the groundwork for the CME
      LiveFutureChainProvider

  * Edits IDataQueueUniverseProvider interface to provide greater
    control to implementors of it

  * Misc. bug fixes required to get FOP data streaming through IB

* Adds comments, adds missing rategate call, and cleans up code

* Force exchange for FOP and Futures when no exchange is provided

* Fixes bug with Portfolio modeling across all asset classes

* Adds LiveOptionChainProvider tests for Future Options

* IB brokerage option symbol bug fixes and improvements

* Fixes contract multiplier lookup bug

  * Fixes issue where we attempted to subscribe to IB data feed with canonical security
  * Adds ES MHDB entry

* Reverts portfolio modeling changes for Futures Options

  * Since IB eats into our account's cash balance when
    a new FOP contract is purchased, we must model by applying funds
    to our cash whenever a new purchase/sell occurs.
    If we choose to model FOPs exactly as we do with futures, we
    will end up with an invalid TotalPortfolioValue on algorithm
    restart. By all means and purposes, FOPs are modeled exactly
    the same as equity options with respect to the portfolio.

  * Adds comments clarifying portfolio modeling and clarifies
    existing portfolio modeling comments with additional context.

* Fixes IB symbol lookup for future options

  * Fixes LiveOptionChainProvider looping 5 times per option chain
    request, even on success

  * Sets OptionChainedUniverseSelectionModel to produce a canonical
    future/future option/option Symbol to avoid creating two Symbols

  * Adds GLOBEX future option symbol mapping from future -> fop

* Fixes LiveOptionChainProvider loading wrong contract option chains

  * Fixes loading of futures options ZIP files when backtesting
  * Adds a string -> decimal JSON converter
  * Additional fixes/refactoring to the LiveOptionChainProvider

* Adds tests for changes to Symbol and LeanData

  * Reverts changes to IB-symbol-map

* Fixes Value for mapped future options tickers

  * Fixes Symbol test

* Changes path of future options to future's expiry date

  * Extra changes made to remove scaling from writing CSV
  * Added method to map from FOP Globex -> FUT Globex

* Fixes MOO and MOC orders for future options

  * Note: this order type might not be supported by IB or CME.

* Bug fixes and updates unit tests

* Update regression tests and data format

* Rebase changes

* 1. Multiple bug fixes for LiveOptionChainProvider, reverts IQFeed changes
2. Address review (partial): Code reuse and cleanup

1.
  * Modifies check in
    `AddFutureOptionShort(Call|Put)ITMExpiryRegressionAlgorithm`
    to ensure no buys have negative quantity

  * Code reuse changes in IB brokerage

  * Bug fix in IB brokerage where we assigned the FOP expiry
    as the futures expiry (requires verification)

  * Doc changes and adds missing summaries/license banners
  * Disposes of HTTP client resources in LiveOptionChainProvider
  * Renames classes and adds FutureOption folder in Common/Securities

2.
  * We revert back to the quotes API for the option chain,
    since the settlement API sometimes had missing strikes.

  * Fixes future option expiry being set as future's expiry
    in LiveOptionChainProvider

  * Fixes bug where wrong option chain was selected because of bad
    expiry lookup in the futures expiries returned from CME

  * Fixes multiple looping bug in LiveOptionChainProvider
  * Adds strike price scaling for LiveOptionChainProvider

  * Reverts IQFeed changes and simplifies interface upgrade changes

  Some additional challenges we'll have to solve as part of FOPs:

    - The `OptionSymbol.IsStandard` method makes the assumption that
      weeklies contracts follow the pattern equities follows, which
      does not apply to Futures Options

    - The Subscription created in:
        `OptionChainUniverseSubscriptionEnumeratorFactory`

      ...adds a Trade config. For illiquid contracts, this
      will delay universe selection for the option symbol
      until we get a trade. However, if we add a quote config,
      the data would instead be loaded based on the first quote
      we received from the brokerage.

      But since we're currently using a trade config, illiquid
      contracts won't start streaming data until it receives a trade.

NOTE: this commit is a WIP to addressing the reviews received in the PR,
but has been committed early for efficiency in the review process

* Fixes regression algorithms and misc. bugs

  * Fixes map file lookup for non-equity options
  * Adds extra assertion at end of algorithm to ensure no holdings are
    left when the algorithm ends.

  * Adds FutureOptionSymbol, allowing all contracts through as standard
  * Changes SPDB to allow defaulting to underlying future symbol
    properties if no entry is found for the given FOP

  * Fixes calls to SPDB in SecurityService, IBBrokerage
  * Reverts AAPL daily ZIP file to fix majority of regression algorithms
  * Adds FOPs symbol properties
  * Fixes existing symbol properties for a few futures
  * Adds tests for changes to Symbol Properties Database

* Removes string SPDB lookup method

  * Updates tests and misc callees of previous method

* Updates all regression tests to use data of already expired contracts

  * Adds Futures Options Expiry Functions tests
  * Adds required futures data for 2020-01-05

* Address review (partial): Expands test coverage and fixes tests

* Set option chain tests parallelism to fixture only

* Fixes broken test for contract month delta for FuturesOptionsExpiryFunctions

* Changes delisting date logic for Futures Options

* Address review: removes duplicate code, misc code fixes

  * Bug fix in MarketHoursDatabase.GetDatabaseSymbolKey() where
    we would use the underlying's Symbol for lookup in the MHDB

  * Adds missing license banner
  * Removes Futures Options entries from MHDB
  * Adds new tests

* Adds SecurityType.FutureOption

  * Converts any underlying comparisons and uses SecurityType directly
    instead for FOP specific behavior

  * Extra code modifications to acommodate new SecurityType

* Addresses review: fixes order fee bug on exercise

  * Additional bug fixes and adding of SecurityType.FutureOption
  * Updates regression algorithms OrderListHash

* Fixes various bugs in IB live implementation

  * Fixes bug setting the right contract expiration date for FOP
    generated by LiveOptionChainProvider

  * Adds new function to FuturesOptionsExpiryFunctions

  * Clarifies parameter names better in some functions/methods

  * Fixes bugs in IB brokerage for FOPs

* Address review - code cleanup and refactor

  * Remove MappingEventProvider, SplitEventProvider, and
    DividendEventProvider for Futures Options in
    CorporateEventEnumeratorFactory

* Address review: Use MHDB key resolver in SPDB

* Makes regression tests pass and adds comment for expiry issue

* Fixes MHDB lookup on string symbol method

* Adds Futures Options greeks regression algorithm (C# only)

* Adds explanitory comment on MHDB FOP lookup

* Remove python from FutureOptionCallITMGreeksExpiryRegressionAlgorithm
2020-12-02 21:49:59 -03:00
Colton Sellers
9d4014b7db Bug 4757 Synchronize Security Additions (#4879)
* Bump time by one tick in adding subscription

* Adjust regressions

* Change removal to immediate

* Use series.AddPoint instead of directly adding it

* Adjust regression

* Add checks for fixed behavior in regressions

* Address review
2020-12-02 21:20:30 -03:00
Adalyat Nazirov
a4f66628fd Lean Optimization interface in QCAlgorithm (#4923)
* initial commit

* run parametrized algorithm with command line parameters

* skeleton: top level structure

* OptimizationNodePacket scheme

* pass parameters as HashSet

* run Lean and read results

* call method on optimization completion

* refactor public interfaces

- close ParameterSet collection; allow only get operations
- explicit method to start LeanOptimizer

* synchronize RunLean method; the result could come in before the backtest id is set in the collections

* another portion of refactoring and interface changes

* comments

* comments & tests for Extremum, Minimization and Maximization classes

* unify optimization paramater values (min, max, step) & mode GridSearch tests

- swap min&max if necessary
- iterate left => right (negate step value if necessary) & provide default step value if step == 0
- no StackOverflow Exception
- parameterSet Id should be global for current generator and retain between steps
- test signle point boundary (min == max)

* BruteForceStrategy tests

* more comments

* Update Optimizer assembly information

- Update Optimizer projects assembly information to match behavior of
  the other projects

* Tweaks

- Adding comments
- Replace OnComplete for Ended event
- Replace Abort for Dispose
- ConsoleLeanOptimizer will keep track of running processes
- Each backtest will store results in a separated directory, so they
  don't fight for the log.txt file.
- Adding cmdline option for lean to close automatically
- Adding concurrent execution backtest limit
- Console optimizer will start Lean minimized
- Escape spaces in Json path

* remove parameter set generator abstraction layer

we don't need this flexibility now.

* refactor public methods; Step shouldn't be public

* constraints: wip

* define contract

* comparison operators and tests

* specify JsonProperty values

* Move SafeMultiply100 to extensions

* Throw exception on failed Optimizer.Start

* constraints: wip

* change finish & dispose process

* minor fixes

- handle force lean abort
- notify consumer if target has been reached

* target & constraints; adapt unit tests

* Minor Tweaks and fixes

- Some logging improvements
- Remove Public since not required

* Ignore empty ParameterValue

* simplify condition

* avoid reinitialization

* reduce type; force immutable

* unit tests for constraints  and target value

* parse & normalize percent values, i.e. 20% => 0.2

* fixup

* Target & Constraint & OptimizationNodePacket unit tests

* Add more json unit tests

- Adding more json conversion unit tests. Fix bug for Extremum which
  wasn't using the converter.

* LeanOptimizer tests

* Estimation results

* User thread safe counters

* LeanOptimizer unit tests; push OptimizationResult on Ended event

* more unit tests

* Minor tweaks

-Estimate ToString in a single line.
-Typos and missing header file

* Add base SendUpdate method

- Add base SendUpdate method for LeanOptimizer

* fix LeanOptimizer test; rely on internal Update rather than timer

* Add OptimizationStatus

- Add missing commments and OptimizationStatus

* EulerSearch implementation: wip

* OptimizationParameter custom converter

* change the type

* make step optional

* change folder structure

* enumerate optimization parameter using IEnumerable & IEnumerator

* unit tests: parameters & objectives

* unit tests: strategies

* remove redundant TODO

* change Euler search boundaries

* more Euler tests

* prevent race condition

* Add account/read endpoint

- Adding account/read endpoint. Adding unit test

* Add status check before running lean

* Minor self review

- Adding missing comments, minor changes

* remove array parameters

* minor changes

- tidy up config file, rename variable
- accept min less or equal than max

* move OptimizationParameter methods to strategies

* Minor improvements for BaseResultHandler derivates

* minor changes

- strict requirements for Step and MinStep values
- strategy specific settigs

* Add TotalRuntime to estimate

Co-authored-by: Martin Molinero <martin.molinero1@gmail.com>
2020-12-02 20:10:40 -03:00
Michael Handschuh
b9974e6f54 Add OptionStrategyMatcher (#4924)
* Reformat/cleanup OptionStrategies

This file was breaking pretty much every style convention in LEAN.
There are other things that should be addressed in here that weren't,
such as passing non-argument names as argument names for ArgumentException,
as well as preferring constructors over property initializer syntax, but
such changes aren't being made to keep this commit strictly reformatting
instead of refactoring.

Added braces and reformatted long lines to make code more legible.

* Add abstract base class for OptionStrategy Option/UnderlyingLegData

This allows us to create either or and later use the Invoke method to push it
into the appropriate list on OptionStrategy.

* Replace O(n) option contract search with 2 O(1) TryGetValue calls

A better improvement would be resolving the correct symbol in the strategy, but
this immediate change is instead just focused on removing the O(n) search inside
a loop.

* Add BinaryComparison and supporting methods in ExpressionBuilder

We're going to use these binary comparisons to make it possible to create
ad-hoc queries against a collection of symbols. Using these expressions,
along with type supporting composition of these expression, we'll be able
to define predicates that can declaratively define how to match an option
strategy with an algorithms current holdings.

* Make GetValueOrDefault defaultValue optional

Was receiving ambiguous invocations leading to neading to invoke this
method explicitly (LinqExtensions.GetValueOrDefault) instead of being
able to use it as an extension method. Making the default value optional
seems to have resolved this ambiguity, leading to cleaner code in the
OptionPositionCollection (forthcoming)

* Add OptionPosition and OptionPositionCollection

OptionPositionCollection aims to provide a single coherent interface
for querying an algorithm's option contract positions and the underlying
equity's position in a performant, immutable way. The immutability of
the type is necessary for how the options matcher will operate. We need
to recursively evaluate potential matches, each step down the stack removing
positions from the collection consumed by each leg matched. This will enable
parallelism of the solution as well as simplifying the mental model for
understanding due to not needing to track mutations to the collection
instance.

* Add Option test class for easily creating option symbol objects

* Add OptionStrategyLegPredicate and OptionStrategyLegDefinition

The definition is a composition of predicates, and each predicate supports
matching against a set of pre-existing legs and a current position being
checked for the next leg (this leg). In addition to the matching functionality,
it also supports filtering the OptionPositionCollection, which is where much
of the work for resolving potential option strategies is done. By successively
filtering the OptionPositionCollection through successive application of predicates,
we wil end up with a small set of remaining positions that can be individually
evaluated for best margin impacts.

All of this effectively unrolls into a giant evaluation tree. Because of this
inherent structure, common in combinatorial optimization, the OptionPositionCollection
is an immutable type to support concurrent evaluations of different branches of
the tree. For large position collections this will dramatically improve strategy
resolution times. Finally, the interface between the predicate and the positions
collection is purposefully thin and provides a target for future optimizations.

* Add OptionStrategyDefinition and OptionStrategyDefinitions pre-defined definitions

The OptionStrategyDefinition is a definitional object provided a template and functions
used to match algorithm holdings (via OptionPositionCollection) to this definition. The
definition defines a particular way in which option positions can be combined in order to
achieve a more favorable margin requirement, thereby allowing the algorithm to hold more
positions than otherwise possible. This ties into the existing OptionStrategy classes and
the end result of the matching process will be OptionStrategy instances definiing all
strategies matched according to the provided definitions.

* Add OptionStrategyMatcher and Options class, w/ supporting types

OptionStrategyMatcherOptions aims to provide some knobs and dials to control how
the matcher behaves, and more importantly, which positions get prioritized when
matching. Prioritization is controlled via two different enumerators, one controller
which definitions are matched first and the other controller which positions are
matched first. Still unimplemented, is computing multiple solutions and running the
provided objective function to determine the best match. When this gets implemented,
we'll also want to implement the timer. For anyone looking to implement these features,
please talk with Michael Handschuh as there's a particular way of representing these
types of combinatorial solutions (a 3D tree) that can be used as a variation of the
linear simplex method for optimizing combinatorial problems.

* OptionStrategyMatcher: Address PR review comments

* Ensure created OptionStrategy legs all have the same multiplier

Each leg definition match gets it's own multiplier which indicates the
maximum number of times we matched that particular leg. When we finish
matching all legs, we pick the smallest multiplier from all the legs in
the definition and use that as the definition's multiplier. When we go
to create the OptionStrategy object we MUST make sure we're using the
multiplier from the definition and not from the individual legs.

This change fixes this issue and also provides a guard clause to ensure
that we're not trying to use a multiplier larger than what was matched.

* Add XML docs for OptionStrategyDefinitions from OptionStrategies
2020-12-02 18:42:24 -03:00
Stefano Raggi
474c5cd890 Update IBAutomater to v1.0.35 (#4984) 2020-12-02 09:56:36 -08:00
Martin-Molinero
0dfb368cb4 Lean shutdown improvements (#4982)
- Use DisposeSafely instead of dipose.
- Adding some logs to know when handlers are getting disposed.
- Normalize exit procedure
2020-12-02 09:55:50 -08:00
Colton Sellers
fbf5300bb6 Block SetWarmUp after Algorithm has compeleted initialization (#4975)
* Fix for #4939

* Address review

* Logic fix
2020-12-02 13:44:04 -03:00
Gerardo Salazar
c67845bd45 Fixes issue parsing SI data w/ unknown enum value (#4961)
* Extends tests to cover new additions
2020-12-01 20:17:25 -03:00
Colton Sellers
54ddbfbe24 Bug 4947 OnOrderEvent exceptions (#4974)
* Force algorithm to fail on runtime error

* add comment

* Remove unneeded change

* Update algorithm summary

* Address review
2020-11-30 15:55:39 -03:00
Gerardo Salazar
bd7be31ede Fixes inability to parse negative strike prices in SecurityIdentifier (#4953)
* Fixes inability to parse negative strike prices in SecurityIdentifier

  * Adds new tests ensuring backwards compat and no throwing w/ negative
    strike prices

* Changes strategy used to support negative strike prices

  * We add support for negative strike prices at the cost of
    reducing the maximum allowed precision for the strike price.
    We encode a negative sign into the 20th bit of the strike price
    and set our bounds for precision to a max (exclusive) of 475712.
    This in turn is then used to form a negative strike when rebuilding
    the SID.

  * Adds tests covering changes

* Address review: adds additional tests and refactors code

* Address self-review: remove unused import in SymbolTests

* Address review: adds additional test cases for OptionStyle and OptionRight

  * These tests are to ensure that backwards compatibility is maintained

* Addresses review: Adds option chain <-> master SID hash test

  * Refactors previous tests to reduce on code duplication

* Reduce test duplication

Co-authored-by: Martin Molinero <martin.molinero1@gmail.com>
2020-11-30 15:45:36 -03:00
Colton Sellers
041a111b92 Implement safe exiting for Lean (#4972) 2020-11-27 18:14:18 -03:00
Stefano Raggi
fa94bcc46a IB Brokerage updates (#4957)
* Update IBAutomater to version 1.0.32

* Update IBAutomater to v1.0.33

* Update IBAutomater to v1.0.34
2020-11-27 14:30:25 -03:00
Athon Millane
2988f7ec3c Bug 4809 Additional Docker Run Script Fixes (#4971)
* Fix typo in data dir volume mount permission.

* Change default notebook launch behaviour for Mac systems.

Co-authored-by: Athon Millane <athon@maxkelsen.com>
2020-11-27 10:06:08 -03:00
Martin-Molinero
b785877d33 Reenable extended market hours sampling (#4967)
* Reenable extended market hours sampling

- Will re enable extended market hours sampling using the last benchmark
  and portfolio value when user exchange were open.

* Add unit test minor tweak

- Improve shutdown latency.
- Setting portfolio initial value
- Adding unit test asserting sample behavior.
2020-11-25 20:39:19 -03:00
Juan José D'Ambrosio
5dbb0db2f9 Update regression algorithms (#4969) 2020-11-25 20:36:17 -03:00
Juan José D'Ambrosio
a489436743 Remove LUNG from blacklisted tickers (#4966) 2020-11-25 19:26:53 -03:00
Colton Sellers
ff2013b0d0 Bug 4864 Missing Brokerage Live Settings (#4958)
* Support Bitfinex live settings

* Add Alpaca and GDAX and their respective tests

* Address review
2020-11-25 10:52:26 -03:00
Colton Sellers
567e7b7679 Change Alpaca default to PolygonDataQueueHandler (#4960) 2020-11-25 10:45:21 -03:00
Martin-Molinero
646f22a63f Remove python stubs directory (#4955)
* Remove python stubs directory

- Removing python stubs directory since after https://github.com/QuantConnect/Lean/pull/4899
it was been replaced by a python package `quantconnect-stubs`.
- Reverting IDE settings using the stubs folder PR https://github.com/QuantConnect/Lean/pull/4657

* Revert "Adds Python stubs location definition for PyCharm and Visual Studio Code (#4657)"

This reverts commit aded66ec5b.

* Address self-review: Provide list of imports and refactor python readme

Co-authored-by: Gerardo Salazar <gsalaz9800@gmail.com>
2020-11-24 12:45:28 -08:00
nazbrok
fd138ec397 GDAXDownloader : save data int the right ticker name (#4799)
* Update crypto entries in SPBD

- Update values for GDAX and Bitfinex, with latest values from exchanges
- Update symbols with >3 letters
- [Fix] Remove  same base-quote entries

* Update unit tests

Increment in precision given smaller lot sizes in SPDB crypto entries.

* Add GDAX symbol properties downloader (unit test)

* Update GDAX symbol properties database

* Add BrokerageSymbol to symbol properties database

* Update GDAX symbol properties

* Address review

- Rename BrokerageSymbol to MarketTicker

* Add GDAX symbol mapper

* Update GDAXBrokerage to use symbol mapper

* Fix GDAX brokerage unit tests

* Replace GDAXSymbolMapper with SymbolPropertiesDatabaseSymbolMapper

* Address review

- use Symbol key in dictionaries

* Rename BrokerageSymbol to MarketTicker

* Save GDAX history in the real ticker folder

* rename tickerMapper to symbolMapper

* fix gdaxdownloader help message

* Save GDAX history in the real ticker folder

* rename tickerMapper to symbolMapper

* fix gdaxdownloader help message

* use SymbolPropertiesDatabaseSymbolMapper

* address review

Co-authored-by: JJD <jjdambrosio@gmail.com>
Co-authored-by: Stefano Raggi <stefano.raggi67@gmail.com>
Co-authored-by: Martin-Molinero <martin@quantconnect.com>
2020-11-23 21:35:39 -03:00
Stefano Raggi
e98b31fc4c Update CoinApi symbol mapping + symbol properties database (#4888)
* Update crypto entries in SPBD

- Update values for GDAX and Bitfinex, with latest values from exchanges
- Update symbols with >3 letters
- [Fix] Remove  same base-quote entries

* Update unit tests

Increment in precision given smaller lot sizes in SPDB crypto entries.

* Add GDAX symbol properties downloader (unit test)

* Update GDAX symbol properties database

* Add BrokerageSymbol to symbol properties database

* Update GDAX symbol properties

* Address review

- Rename BrokerageSymbol to MarketTicker

* Add GDAX symbol mapper

* Update GDAXBrokerage to use symbol mapper

* Fix GDAX brokerage unit tests

* Replace GDAXSymbolMapper with SymbolPropertiesDatabaseSymbolMapper

* Address review

- use Symbol key in dictionaries

* Rename BrokerageSymbol to MarketTicker

* Update unit test

* Add Bitfinex symbol properties downloader (unit test)

* Add Bitfinex symbol market ticker to downloader

* Update Bitfinex symbol properties database

* Update BitfinexBrokerage to use SymbolPropertiesDatabaseSymbolMapper

* Remove Bitfinex test symbols from db

* Update Binance symbol properties database

* Update BinanceBrokerage to use SymbolPropertiesDatabaseSymbolMapper

* Add missing Binance test case

* Update symbol properties database

- gdax, bitfinex, binance

* Update CoinApi symbol mapper for new SPDB

* Exclude Bitfinex BCH pre-2018-fork in CoinApiSymbolMapper

* Remove unused properties

* Add CoinApi mappings

Co-authored-by: JJD <jjdambrosio@gmail.com>
2020-11-23 19:15:31 -03:00
Stefano Raggi
d2eae2f652 IB Brokerage Updates (#4945)
* Update C# API DLL up to  Oct 30, 2020

- a8e66989e5

* Add extra account information logging after connect
2020-11-23 11:53:28 -03:00
Gerardo Salazar
0f0a2bc9a8 Fixes weeklies parsing, causing certain futures to be inaccessible in QCAlgorithm (#4936)
* Fixes weeklies parsing, causing certain futures to be inaccessible in Algorithm

  The FuturesExpiryFunction expects the contract month of the Future,
  not the expiration. As a result, the contract gets filtered as a
  weekly contract, rather than as a standard due to the discrepancy
  between the expiry dates when the contract month differs from the
  expiry date's month.

  A very important fact to note is that futures can and do expire prior
  to the contract month. BZ,(brent crude financial futures) expire two
  months prior to the contract month, CL one month prior, etc.

  There has been an addition that contains a "reverse" futures expiry function
  lookup table. We use this to lookup the contract month to re-calculate
  the Future expiry.

  This PR also fixes dairy and adds extra expiry dates. Dairy can have
  an expiry *after* the contract month, so a new path was added to the
  SymbolRepresentation to ensure that these contracts are loaded
  correctly.

* Address review: Adds tests and fixes bug in SymbolRepresentation

  * Updates SID comment on `Date` property to reflect fact that we use
    future expiry for its value

  * Fixes bug in SymbolRepresentation where expiration day would always
    be 01 when parsing a contract with an expiration after the contract
    month

  * Fixes bug in SybmolRepresentation where expiration year would be
    four digits long when parsing a contract with an expiration after
    the contract month

  * Fixes some bad dairy expiry dates

  * Adds tests for SymbolRepresentation and the futures filtering for
    standard contracts

  * Renames method used to extract delta between contract month and
    expiry date

* Removes GH comment and restores Futures contract month expiry param
2020-11-20 11:58:49 -03:00
Martin-Molinero
8ad81dca71 Update readme.md (#4949) 2020-11-20 09:12:23 -03:00
James Kardatzke
f965f34a3f Added Quiver API token variable to Config (#4946)
* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Delete QuiverWikipedia.cs

* Delete QuiverWallStreetBets.cs

* Delete QuiverDataDownloader.cs

* Delete QuiverWallStreetBetsDataDownloader.cs

* Delete QuiverWikipediaDataDownloader.cs

* Delete QuiverDataAlgorithm.cs

* Delete QuiverHouse.cs

* Delete QuiverPoliticalBeta.cs

* Delete QuiverSenate.cs

* Delete QuiverHouseDataDownloader.cs

* Delete QuiverPoliticalBetaDataDownloader.cs

* Delete QuiverSenateDataDownloader.cs
2020-11-18 20:32:03 -03:00
Colton Sellers
06a7d54c38 run_docker_notebook bat hotfix (#4943) 2020-11-17 21:40:29 -03:00
Colton Sellers
7174fcb9d7 RoundDown fix for PeriodCountConsolidatorBase (#4940)
* Limit rounding interval to 1 day, add SubtractRoundDown and AddRoundUp functions

* fix error message

* Only subtract round down if period is greater than a day

* Tests

* Comment clarification

* Pivot solution to simple fix

* fix error message

* Remove RoundDown/Up limitations; add remarks

* address review
2020-11-17 21:40:10 -03:00
IlshatGaripov
29e9d678f2 Bug 4815 iex web socket library (#4914)
* Fixes 4815 by loading the requested assembly from different folder.

# Conflicts:
#	ToolBox/Program.cs

* Upgrades System.Collections.Immutable to Version=1.2.5.0

* Creates a prototype for SSE streaming in IEXDataQueueHandler.

* Revert the changes in Tick.cs

* Implements a logic in IEXDataQueueHandler that updates the data-feed subscription after Subscribe/Unsubscribe

* Implements IEXCouldSubscribeMoreThan100Symbols - which fails and other small fixes.

* Implements DoForEach LinqExtensions

* Implements IEXEventSourceCollection that wraps all logic that is SSE-subscriptions and symbol-limits-per-connection concerned.

* Changes:

1) Fixes to address review.
2) Makes IexMarketPercent in QuoteSSE nullable as null values are assigned to in this field in data object received before the traing session start.
3) Deprecates helper Subscribe/Unsubscribe in IEXDataQueueHandler and IEXCouldSubscribe test.

* Fixes:

1) _refreshEvent.Reset() order was not correct - should be called before UpdateSubscription
2) ProcessJsonObject- leaves only the functionality to emit ticks.
3) IEXEventSourceCollection - replaces int counter with CountdownEvent to improve the logic - in particular, need a mechanism that would not allow the repeated call to continue until the first one is completed

* Refines the logic with parsing a data snapshot.

* Fixes few more bugs:

1) Logic in ProcessJsonObject
2) Logic in UpdateSubscription - need to introduce additional ManualResetEvent to implement the intended logic - otherwise the logic is not suitable for general case

* Introduce rate-gate limit in IEXEventSourceCollection:

because when subscribing to a bunch of shares (more than 200 for example)
the violation of rate gate policy may occur, which described in API docs asRequest Limits

IEX Cloud only applies request limits per IP address to ensure system stability.
We limit requests to 100 per second per IP measured in milliseconds, so no more than 1 request per 10 milliseconds.
SSE endpoints are limited to 50 symbols per connection. You can make multiple connections if you need to consume more than 50 symbols.:

* Few additional fixes done after real time testing

* Adds xml-docs in stream response object + renaming a file.

* Fixes:

1) Additional StreamResponseStocksUS parsing issues, that can happen outside of regular exchange hours.
2) Cancel clientUpdateThread by means of CancellationTokenSource
3) Replace BuildSymbolsQuery by string.Join

* Fixes:

1) Changing Log Trace -> Debug
2) Adds ConfigureAwait(false) to async method call
3) Removes direct reference to System.Net.Http

* Removes a task and manual reset event in IEXEventSourceCollection

* Additions:

1) IEXEventSourceCollectionSubscribes test
2) GetSnpStocksArray() helper method
3) Installs packages in QC.tests : HtmlAgilityPack & LaunchDarkly.EventSource

* IEX history provider fixes :

1) Tiny bug in ProcessJsonObject - use continue instead of return as execution is inside the for-each block)
2) Adds period variable for the historical data retrieved
3) Fixing from  if (date.Date < start.Date || date.Date > end.Date)  conditional check -->  if (date < start || date > end)  for more precise sorting.

* Changes:

1) Removes HtmlAgilityPack and SNP scraper
2) Uses hard coded symbols instead

* Bug fix:

 - at certain hours (example: before pre-market open or on holidays) IEX may send no data on subscription - when trying to connect during those hours Message handler may not be fired - need to place the counter signal to client.Opened to be informed of successful connect.

* Implements:

1) IEXEventSourceCollectionSubscriptionThoroughTest and MockedIEXEventSourceCollection
2) Makes changes to IEXEventSourceCollection accordingly to allow the thorough testing.

* Fixes formatting issue in StreamResponseStocksUS

* Small fix for a new tests:

- Change RemovedClientSymbols to keep not clients itself, but symbols array, because clients are being disposed right further

* Enables extended logging in Toolbox.

* Fixing IEX historical data fetcher bugs:

1) Bug in IEXDataDownloader.cs - HistoryRequest not precisely correct.
2) Enables day-by-day daily bar downloading in IEXDataQueueHandler.
Motivation: Suppose we need data for some interval in the past - from-date=20170915-00:00:00 --to-date=20171103-00:00:00.
With current behavior IEX would have to download all the historical data from =20170915-00:00:00  up to this day.
3) Extends SynchronizingHistoryProvider

* Enables async fashion historical data download

* More fixes to IEXCouldGetHistory test.

* Reverts day-by-day daily bar downloading and other fixes.

* Removes needless packages & references

* Fix package reference

* To address review

* Sort out zero price ticks:

after testing on real-time algo 30 min before the market open now - IEX may send updates for many securities with zero lastPrice, lastSize - fix to sort such entries out

* Workaround for missing QuoteTicks timestamps:

Since we don't have a stamp for quote tick updates (only for trades) we calculate the average delay between trade tick's time stamp and local time, and
assuming that delay in average is the same for quote updates - just assign the local machine time adjusted for this average

* Simplifies the things.

* Changes:

1) Deprecates quote updates for IEX stocks.
2) Reduce the stream updates to reduce costs to ->
# Stock Quotes every 1 second (per symbol? )
# Can be up to 54,000 messages per symbol per day
https://iexcloud.io/docs/api/#how-messages-work

* Fixes:

1) IEXDataQueueHandler: give an error message on extended market hours or tick resolution subscription request. As they are not really well supported by IEX.
2) Few small fixes in IEXEventSourceCollection, including additional condition for when the subscription remains irrevocable.
2020-11-16 21:10:14 -03:00
Colton Sellers
098ac7d0a9 Wider range of offsets on Month tests (#4935) 2020-11-16 15:15:32 -08:00
Stefano Raggi
6cb4411f6e Update IBAutomater to v1.0.31 (#4932)
- add window handler for unsupported IBGateway versions
2020-11-16 11:36:34 -03:00
James Kardatzke
46ef9a9dbb Quiver Quantitative Integration (#4869)
* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Delete QuiverHouseDataDownloader.cs

* Delete QuiverSenateDataDownloader.cs

* Delete QuiverPoliticalBetaDataDownloader.cs

* Add files via upload

* Delete QuiverHouse.cs

* Delete QuiverSenate.cs

* Delete QuiverPoliticalBeta.cs

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Delete QuiverDataAlgorithm.cs

* Add files via upload

* Add files via upload

* Add files via upload

* Addresses self review: Cleans up code and adds new unit tests

  * Adds Quiver* C# files to project
  * Adds new unit test for QuiverCongress
  * Adds Python algorithm example

* Address self reviews

- Adding some missing xml docs
- Removing unrequired imports.
- Minor rename from Date to ReportDate
- Live trading will throw InvalidOperationException

* Fixes for example algorithms

Co-authored-by: Gerardo Salazar <gsalaz9800@gmail.com>
Co-authored-by: Martin Molinero <martin.molinero1@gmail.com>
2020-11-13 20:07:17 -03:00
Colton Sellers
d8d8134437 Support Offset in DateRules MonthStart/End WeekStart/End (#4916)
* Add offset capabilities and related tests

* Add tests for Weekend Offsets on Symbol DateRules

* Unify Iterator Behavior

* Refactor and consolidate functions to reduce duplicate code

* Positive offset values only

* Address review

* Expand Month tests to include Forex and Crypto cases

* Ensure order of days in schedule

* Refactor and unify behavior

* More edge cases and tuning

* Address review + more tests
2020-11-13 15:38:34 -03:00
Jasper van Merle
9a30c9bd5f Enable logging when building and uploading stubs (#4933)
* Enable Twine logging

* Enable setup.py logging
2020-11-12 17:42:09 -08:00
Jasper van Merle
f76a0efb0e Use new generator to generate stubs and publish to PyPI (#4899)
* Use new generator to generate stubs and publish to PyPI

* Hide twine stdout and stderr to prevent token from leaking
2020-11-12 13:27:00 -08:00
Colton Sellers
7238fcd0f3 Bug 4809 Docker Run Script Fixes and Improvements (#4922)
* Refactor and hotfix run script

Removed dependency on `realpath`, notably absent from macOS and some debian distros. Replaced with an equivalent bash function.

Cleaned up prompt response handling, replaced var tests with param expressions.

Removed potentially uneeded calls to sudo (again absent on some systems and most users are part of the docker group anyway). Instead, test if it's needed and get preauth with sudo -v.

Poll for running and stopped Lean containers, and prompt before replacing them. Would fail prior.

Uppercased variabled. Sorry.

* Revert docker output redirection to stderr

* Adjustments to maintain all functionality

* Mimic behavior across run scripts

* Update readme to reflect Docker script changes

* Ignore results storage

* Correct print statement

* Mirror changes on research docker scripts

* Make executable

* Handle already running container and sudo privs

* Fix for issues found in testing

* Address Review

* Small adjustments found in linux testing

* Doc improvement

* Add auto update option for Docker images

* Fix image var reference

Co-authored-by: Peter Kazazes <peter@peterk.co>
2020-11-11 15:48:02 -03:00
Martin-Molinero
91e8393aac DividedEventProvider distribution computation (#4828)
* DividedEventProvider distribution computation

- Update regression algorithm which was using a different reference
  price when calculating the dividend
- Adjust divided event provider to compute distribution using factor
  file reference price, if not 0. Adding unit tests
- For equities, only emit auxiliary data points for
  TradeBar configurations, not for QuoteBars, nor internal.

* Address reviews

- Split and Dividend event provider will throw an exception when there
  is no reference price available. Updating `wm` factor file which was
  missing references price and regression algorithms using WM.
- Updating unit tests asserting new exception
2020-11-11 15:47:51 -03:00
Stefano Raggi
5771635265 Fix Base Account Currency message for PaperBrokerage (#4929)
- the displayed message was not showing the default currency (USD)
2020-11-10 17:46:35 -03:00
Stefano Raggi
914486fdb6 Fix OutOfRangeException in Bitfinex subscriptions (#4926) 2020-11-09 16:42:22 -03:00
Martin-Molinero
61fda8b62c Protobuf will use recyclable memory stream (#4921)
* Protobuf will use recyclable memory stream

- Serialization will reuse recyclable memory streams
- Remove serialization of exchange and sale condition for ticks.
  Updating unit tests.
- There is no need to serialize BaseData.EndTime, covered by unit tests.

* Tick will keep a parsed sale condition property

* Readd tick exchange. Json ignore ParsedSaleCondition
2020-11-06 20:59:25 -03:00
Stefano Raggi
124ac3b98e Fix FxcmBrokerage GetOpenOrders returning empty (#4917) 2020-11-06 10:35:11 -03:00
Colton Sellers
9dca43bccb Expand Filtering of Contracts by type to Futures (#4891)
* Implement future type filter

* Filter weeklys test

* Fix for test, contracts were being filtered out by new type filter

* Share core contract filtering logic in new base class

* Catch Future symbols we don't have Expiry functions for

* Expand tests for new filtering

* Address review

* Small doc change

* Compare Date component for expiry

* Clarifying comment
2020-11-04 20:55:33 -03:00
Alexandre Catarino
6efeee07dc Implements Equity Fill Model (#4913)
* Implements Equity Fill Model

This commit sets the base to create a new equity fill model and the `EquityFillModel` is just a copy of `FillModel`.

* Adds Summary to FillModelPythonWrapper.GetPricesInternal

Adds summary to FillModelPythonWrapper.GetPricesInternal with remarks that it's a temporarily method to help the refactoring of fill models.
2020-11-04 15:51:41 -03:00
Andreas Sundebo
c452dd3726 Feature 4893 performance improvements (#4895)
* Change GetSubscriptionDataConfigs to return IEnumerable<SubscriptionDataConfig>

* Move localTime outside loop

* Remove legacy code

* Remove unnecessary OrderBy

* Reorder conditions to reduce number of times Contains() is called

* Revert "Remove unnecessary OrderBy"

This reverts commit 85383b062e.

* Revert "Change GetSubscriptionDataConfigs to return IEnumerable<SubscriptionDataConfig>"

This reverts commit cbd97c9f36.
2020-11-03 19:35:36 -03:00
Stefano Raggi
c602fd0a3f Fix ambiguous future symbol error in IB brokerage (#4912)
* Fix ambiguous future symbol error in IB brokerage

* Address review

- Fix symbol mapping
2020-11-03 14:44:17 -03:00
Stefano Raggi
104071cda5 Do not terminate algorithm for exceptions in GDAX Fill Monitor (#4910)
* Do not terminate algorithm for exceptions in GDAX Fill Monitor

* Address review

- Only emit warning for REST API errors, other errors are fatal
2020-11-02 21:01:21 -03:00
Aaron Janeiro Stone
a5d9526d65 Add defaults (as comments) to Python.Runtime (#4904)
* Redone defaults for python runtime dll config and readme

* Update Python.Runtime.dll.config

Co-authored-by: Jared <jaredbroad@gmail.com>
2020-11-02 15:02:59 -08:00
Juan José D'Ambrosio
8e525c63fc Fix issue in TiingoNews converter if tickers contains space (#4908)
Also, if ticker contains pipe ( "|" )  it'll be ignored.
2020-11-02 13:58:39 -03:00
Stefano Raggi
d0e9134cc9 IB Brokerage Updates (#4900)
* Add RequestId to request information logging

* Use unique request id across all request types (orders, subscriptions, data queries)

- Previously we had three separate counters for request types and this was causing request information messages to be overwritten (different request types with same ids)

* Update GetContractDetails to log all contracts found
2020-11-02 10:58:12 -03:00
Gerardo Salazar
883d354a98 Adds DC future contract symbol mapping for IB (#4905)
* Fixes SPDB entry for Class III/IV Milk
2020-11-02 10:57:57 -03:00
Colton Sellers
76a53eb096 Local Object Store Refactor and Fixes (#4880)
* Store temp files in subdirectory

* Fix Dispose case for new temp dir

* Adjust tests for new temp dir

* Dispose unit tests

* Unit test for issue 4811

* Refactor for not using temp files

* Fix storage checks for saving data, plus tests

* Use Base64 for storing keys and decoding them; handles odd key strings

* Don't allow "?" in a key

* Address review

* Deleted test cases

* PersistData handle deletion of files

* Refactor GetFilePath to use Persist()

* Make PathForKey protected
2020-10-30 21:36:23 -03:00
Colton Sellers
c02ee1b0d8 QB Set Start Date Relative to Data Release (#4894)
* Set start date relative to timezone

* Address Review
2020-10-30 21:20:32 -03:00
Colton Sellers
9167882ab2 Feature Notebook Api Support (#4898)
* Modify Notebook scripts to load API instance

* Modify docker to move script to IPython profile

* Fix dockerfile copying of start.py

* Fix break for C# cloud and docker load

* Unnecessary path finding

* Update example notebooks

* Update documentation

* Address review

* Adjust readme

* Poor choice of words
2020-10-30 21:05:09 -03:00
Mathieu Paquette
854b987cd0 feat(ToolBox\IQFeed): prevent unordered ticks to be processed (#4884)
closes #4649
2020-10-27 20:30:46 -03:00
Juan José D'Ambrosio
a26414d273 Add decimal places as parameters to get dividends with arbitrary precision (#4883)
* Add decimal places as parameters to get dividends with arbitrary precision

 
Also, increase precision when generating strings from factor files rows.

* Add xml documentation entries for new optional arguments.
2020-10-27 20:30:17 -03:00
Alexandre Catarino
84264ca7ef Adds CustomBuyingPowerModelAlgorithm (#4824)
* Adds CustomBuyingPowerModelAlgorithm

This algorithms is an example on how to implement a custom buying power model.

In this particular case, it shows how to override `HasSufficientBuyingPowerForOrder` in order to place orders without sufficient buying power according to the default model.

* Upgrades CustomModelsAlgorithm to Include CustomBuyingPowerModel

The custom buying power model overrides `HasSufficientBuyingPowerForOrderResult` but it doesn't change the trades and, consequently, the regression statistics.
2020-10-21 17:27:30 -07:00
Stefano Raggi
b2ed398687 Allow account currency to be overridden by the algorithm (#4856)
* Allow account currency to be overridden by the algorithm

* Fix failing unit test

* Address review

- Revert removal of call check in SecurityPortfolioManager.SetAccountCurrency
- Revert changes to unit tests
- IBrokerage.AccountBaseCurrency now defaults to null
- BrokerageSetupHandler will not change the algorithm's account currency if the brokerage returns null, allowing the algorithm to call SetAccountCurrency in Initialize
2020-10-20 14:17:16 -03:00
Martin-Molinero
e8c316cbcf Fix Toolbox tickers parsing (#4876)
- Fix ToolBox tickers parsing, adding unit test.
2020-10-20 11:27:41 -03:00
Colton Sellers
724e52c0b3 Make StartDate relative to Algorithm TimeZone in Live mode (#4871) 2020-10-19 15:44:08 -03:00
Jared
4252c79e45 Create QuantConnect-Platform-2.0.0.yaml
Initial commit of QuantConnect Platform Yaml.
2020-10-18 17:46:13 -07:00
Martin-Molinero
cbb40dfa43 Ignore composer ThreadAbort Exception (#4870)
- Composer inner task will not log exception if it's of type Thread
  abort, which means we are shutting down.
2020-10-16 10:37:14 -03:00
Colton Sellers
8792fa2600 Standardize API.cs to use JSON Objects (#4868)
* Standardize API to use JSON Objects

* Address review
2020-10-15 20:24:55 -03:00
Louis Szeto
20e9fd7899 bug-#4846-Fail on restart investing after liquidation on MaximumDrawdownPercentPortfolio.py (#4847)
* Fail on restart investing after liquidation

I added a line so that the trailing high value could be rebalanced and the investment process won't be stop by high value always more than current value by drawdown percent.

* Update MaximumDrawdownPercentPortfolio.py

* Fix for MaximumDrawdownPercentPortfolio

- Fix C# MaximumDrawdownPercentPortfolio to reset portfolio value after
  liquidation. Only reset once we have actually adjusted some targets.
  Updating regression algorithms.

Co-authored-by: Martin Molinero <martin.molinero1@gmail.com>
2020-10-15 13:34:08 -03:00
Colton Sellers
e05a6bffd0 Bug 4835 api tests failing (#4838)
* Remove F# Project, Splits, and Dividends Tests

* Separate tests that require external accounts; read from config

* Removal of non supported "prices" endpoint test

* Removal of unsupported API functions

* Address review

* NOP GetLastPrice for removal of Prices endpoint

* Post rebase fix

* Rebase fix 2

* remove /r/n from eof for api tests

* Reflect similar refactors to NodeTests

* Fix for live algorithm API testing

* Address Review
2020-10-15 13:31:53 -03:00
Colton Sellers
c2f0fdc47a Bug #4839 Docker Bash Script Hotfix (#4861)
* fix IFS issue in bash docker scripts

* Add default image to research config
2020-10-14 13:20:01 -03:00
Michael Handschuh
b1b8da1e17 Fixes Market Simulated Automatic Option Assignment (#4853)
* Add underlying holdings to regression result handler details log

When debugging option exercise/assignment issues it's useful to see the
underlying holdings at the time the option contract fill event is processed.

Also adds the full symbol string to the top of the order event section.
The Symbol.Value was being logged via OrderEvent.ToString(), but it wasn't
the full SecurityIdentifier - by including the full SID string it makes it
easier to correlate fills over symbol rename boundaries.

* Fix automatic option assignment from market simulation

During the recent OptionExerciseOrder.Quantity refactor, this case was missed.
Additionally, it was realized that there were no regression tests covering the
automatic assignment via the market conditions simulation. This change introduces
a regression algorithm that covers the automatic assignment of put/call options.

* Update BasicOptionAssignmentSimulation._rand to be non-static

If this value is static then we reuse the same Random instance for ALL regression
tests, thereby defeating the purpose of using a well known seed number. This means
we get different results based on the order execution of preceding algorithms.
By making this an instance variable each algorithm will start with the same seed
value, ensuring consistent runs between regression tests, either run as a suite or
running a single algorithm in isolation.
2020-10-13 19:39:25 -03:00
Stefano Raggi
01a0454c57 Fix Bitfinex Liquidate error with AccountType.Cash (#4852)
* Move updating of cashbook for fees out of SecurityPortfolioModel

* Update BitfinexBrokerage to handle fees in base currency
2020-10-13 15:34:44 -03:00
Stefano Raggi
90e2c48404 Remove Oanda API v1 (deprecated) (#4833) 2020-10-12 16:48:42 -03:00
Aaron Janeiro Stone
888c443264 Moves cash brokerage/IExecutionModel test to post-init (#4826) 2020-10-12 11:14:20 -03:00
adam-may
03efc1b735 Remove internal usages of implicit operator in Indicator code (#4844)
* Convert usages of implicit operator in IndicatorBase and IndicatorDataPoint

* Reverting changes to example code
2020-10-12 10:31:05 -03:00
Martin-Molinero
6ef2ead929 Do not update price scale for fillforward data & IsFillForward flag fix (#4836)
* Do not update price scale for fillforward data

- Do no update price scale for fill forward data. FillForward data
  should keep using the prev scale for which it was created. Adding unit tests
- When cloning do not lose IsFillForward flag state, affects
QuoteBars/Ticks, does not affect TradeBars since they perform a memberwise clone.
Adding unit tests

* Auxiliaries shouldn't really affect on applied price factor scale.

Despite we can receeive FillForward'ed data points, corresponding
Auxiliaries for them are not FillForward so we do meet the condition
and then refresh price factor. As a result all futher FF data points are scaled too.

* Regression algorithm to check that FillForward'ed data points arrived with last real price factor

* Add trade for regression algorithm

- Minot tweaks and adding trade for new regression algorithm.
- Updating AddOptionContractExpiresRegressionAlgorithm because it is
  using the symbol for which new data was added.

Co-authored-by: Adalyat Nazirov <aenazirov@gmail.com>
2020-10-09 18:09:30 -03:00
Martin-Molinero
bfd319c91e OptionChain and OptionContract improvements (#4804)
* OptionChain and OptionContract improvements

- QCAlgorithm.AddUniverse will return the added Universe instance.
- Adding new OptionChainedUniverseSelectionModel will monitor a Universe changes
  and will spwan new OptionChainUniverse from it's selections. Adding
  regression test Py/C#.
- Adding new OptionContractUniverse that will own option contracts and
  their underlying symbol. Adding regression test
- Fix double notification for security changes, bug seen in updated
  UniverseSelectionRegressionAlgorithm
- Remove UniverseSelection special handling for Option and Future chains
- Fix DataManager not removing SubscriptionDataConfigs for Subscriptions
  which finished before being removed from the universe
- Refactor detection of user added Universe so that they do not get
  removed after calling the UniverseSelectionModel

* Add check for option underlying price is set

* Address reviews

- Adding python regression algorithm for
  `AddOptionContractFromUniverseRegressionAlgorithm`
  and `AddOptionContractExpiresRegressionAlgorithm`
- Rename QCAlgorithm new api method to `AddChainedOptionUniverse`

* Fix universe refresh bug

- Fix bug where a universe selection refresh would cause option or
  future chain universes from being removed. Adding regression algorithm
  reproducing the issue.

* Rename new option universe Algorithm API method

- Rename new option universe Algorith API method from
  AddChainedOptionUniverse to AddUniverseOptions
- Rebase and update regression test order hash because of
  option expiration message changed
2020-10-09 10:52:50 -03:00
Stefano Raggi
5f61456df8 Set Account Base Currency from Brokerage in Live Mode (#4806)
* Add property IBrokerage.AccountBaseCurrency

* Set AccountCurrency to brokerage AccountBaseCurrency

* Remove USD AccountCurrency check

* Fix Oanda account base currency

* Fix currency symbol in CashBook.ToString()

* Fix unit tests

* Address review

* Add DebugMessage when changing account currency

* Add debug message for brokerage account base currency

* Fix currency symbol in equity chart and runtime statistics

* Update unit tests
2020-10-09 09:58:01 -03:00
Michael Handschuh
a46a551c03 Include Order.Tag/OrderEvent.Message in their ToString, Fix default tag values (#4797)
* Improve information tracked in regression's {algorithm}.{lang}.details.log

The details.log file aims at providing a diff-able document that quickly and
easily provides actionable information. Since many regression algorithms use
the algorithm's debug/error messaging facilities to log various pieces of algo
state. This document also support a configuration option: regression-high-fidelity-logging'
that logs EVERY piece of data, again, with the aim of providing an easily diff-able
documenbt to quickly highlight actionable information. I may have missed omse key
pieces of information here, but now that the entire QC knows about this regression
tool, if additional information is required then hopefully it's easy enough at this
point to extend the RegressionResultHandler to suit our needs.

The RegressionResultHandler was initially implemented to provide a concise log of
all orders. This was achieved by simply using the Order.ToString method. While
testing/investigating OptionExerciseOrder behavior, it became evident that more
information was required to properly identify the source of potential failures or
differences between previous regression test runs. This change adds logging for
almost every IResultHandler method and additionally attempts to capture the
actual portfolio impact of every OrderEvent. This is accomplished by logging
the portfolio's TotalPortfolioValue, Cash properties and the security's
SecurityHolding.Quantity property.

This change also standardizes the timestamps used to folloow the ISO-8601 format.

When using the RegressionResultHandler, it is highly recommeded to also disable
'forward-console-message' configuration option to ensure algorithm Debug/Error
message logging is done synchronously to ensure correct ordering with respect to
log messages via Log.Debug/Trace/Error.

* Fix typo in options OrderTests test case name

* Update SymbolRepresentation.GenerationOptionTickerOSI to extension method

Far more convenient as an extension method

* Improve R# default code formatting rules

Many of these rule changes focus on improving the readability of code,
with a particular emphasis on multi-line constructs, chained method calls
and multi-line method invocations/declarations.

* Add braces, use string interpolation and limit long lines

* Refactor OptionExerciseOrder.Quantity to indicate change in #contracts

For all other order types, the Order.Quantity indicates the change in the algorithm's
holdings upon order execution for the order's symbol. For OptionExerciseOrder, this
convention was broken. It appears as though only exercise was initially implemented,
in which case only long positions were supported and a code comment indicated that
only positive values of quantity were acceptable, indicating the number of contracts
to exercise. At a later date, assignment simulation was added and utilized a negative
order quantity. This caused some major inconsistencies in how models view exercise
orders compared to all other order types. This change brings OptionExerciseOrder.Quantity
into alignment with the other order types by making it represent the change in holdings
quantity upon order execution.

This change was originally going to be much larger, but in order to minimize risks and to
make for an easier review experience, the additional changes will be committed separately
and pushed in their own PR. Some of the issues identified include:
* Manual Exercise (especially for OTM) is not covered
* Margin Calculations (in particular taking into account opposing contracts held)
* IBrokerage.OptionPositionAssigned is raised for exercise (later filtered by tx handler)

Fixes OptionPortfolioModelTests to use exercise model to properly model exercise of
non-account quote currency option contract.

* Include Order.Tag/OrderEvent.Message in their ToString, Fix default tag values

There was inconsistencies in what we were checking for. The order constructors
default the tag parameter to an empty string but Order.CreateOrder checks for
a null string. Additionally, the order constructors (limit,stopmarket,stoplimit)
would check for an empty string and if so, apply a default order tag.

This change cleans these checks up using string.IsNullOrEmpty and also removes the
check from Order.CreateOrder since we're passing the tag into the various order
constructors.
2020-10-08 21:54:54 -03:00
Michael Handschuh
cf9b547e2e Refactor OptionExerciseOrder.Quantity to be consistent with other Order types (#4796)
* Improve information tracked in regression's {algorithm}.{lang}.details.log

The details.log file aims at providing a diff-able document that quickly and
easily provides actionable information. Since many regression algorithms use
the algorithm's debug/error messaging facilities to log various pieces of algo
state. This document also support a configuration option: regression-high-fidelity-logging'
that logs EVERY piece of data, again, with the aim of providing an easily diff-able
documenbt to quickly highlight actionable information. I may have missed omse key
pieces of information here, but now that the entire QC knows about this regression
tool, if additional information is required then hopefully it's easy enough at this
point to extend the RegressionResultHandler to suit our needs.

The RegressionResultHandler was initially implemented to provide a concise log of
all orders. This was achieved by simply using the Order.ToString method. While
testing/investigating OptionExerciseOrder behavior, it became evident that more
information was required to properly identify the source of potential failures or
differences between previous regression test runs. This change adds logging for
almost every IResultHandler method and additionally attempts to capture the
actual portfolio impact of every OrderEvent. This is accomplished by logging
the portfolio's TotalPortfolioValue, Cash properties and the security's
SecurityHolding.Quantity property.

This change also standardizes the timestamps used to folloow the ISO-8601 format.

When using the RegressionResultHandler, it is highly recommeded to also disable
'forward-console-message' configuration option to ensure algorithm Debug/Error
message logging is done synchronously to ensure correct ordering with respect to
log messages via Log.Debug/Trace/Error.

* Fix typo in options OrderTests test case name

* Update SymbolRepresentation.GenerationOptionTickerOSI to extension method

Far more convenient as an extension method

* Improve R# default code formatting rules

Many of these rule changes focus on improving the readability of code,
with a particular emphasis on multi-line constructs, chained method calls
and multi-line method invocations/declarations.

* Add braces, use string interpolation and limit long lines

* Refactor OptionExerciseOrder.Quantity to indicate change in #contracts

For all other order types, the Order.Quantity indicates the change in the algorithm's
holdings upon order execution for the order's symbol. For OptionExerciseOrder, this
convention was broken. It appears as though only exercise was initially implemented,
in which case only long positions were supported and a code comment indicated that
only positive values of quantity were acceptable, indicating the number of contracts
to exercise. At a later date, assignment simulation was added and utilized a negative
order quantity. This caused some major inconsistencies in how models view exercise
orders compared to all other order types. This change brings OptionExerciseOrder.Quantity
into alignment with the other order types by making it represent the change in holdings
quantity upon order execution.

This change was originally going to be much larger, but in order to minimize risks and to
make for an easier review experience, the additional changes will be committed separately
and pushed in their own PR. Some of the issues identified include:
* Manual Exercise (especially for OTM) is not covered
* Margin Calculations (in particular taking into account opposing contracts held)
* IBrokerage.OptionPositionAssigned is raised for exercise (later filtered by tx handler)

Fixes OptionPortfolioModelTests to use exercise model to properly model exercise of
non-account quote currency option contract.
2020-10-08 20:05:20 -03:00
Colton Sellers
faa4e91e04 Test 4249 fix regression algorithms executed together (#4832)
* Fix for running regressions algorithms together

* Remove unneeded changes
2020-10-08 19:59:06 -03:00
Michael Handschuh
cc83f19528 Rename QuantConnect.API to QuantConnect.Api (#4830)
Also renames API folders to Api
2020-10-08 19:35:57 -03:00
Reginald Louis
54af12b06a Fix DirectoryNotFoundException on linux/mac (#4829) 2020-10-07 20:19:01 -03:00
Michael Handschuh
1d1c8f5f82 Don't raise IBrokerage.OptionPositionAssigned on exercise (#4801) 2020-10-07 14:40:34 -03:00
Colton Sellers
3966c0e91f Market Hours Database Adjustment (#4818)
* Fix Entries

* Fix options dates
2020-10-06 20:45:03 -03:00
Gerardo Salazar
28160e1301 Fixes generation of explicit "null" value in Smart Insider Transactions/Intentions (#4817)
* Adds new unit tests covering changes and testing for old case

  * JsonConvert.SerializeObject would convert a `null` value into a
    literal string of "null" when writing to a file via the ToLine
    abstract method. We opt for an empty string whenever the underlying
    value is null so that the parsing works correctly later in the
    data loading cycle.
2020-10-06 10:20:59 -03:00
Adalyat Nazirov
f4679785a5 Pass command line parameters values as string (#4819)
* avoid preliminary typing command lines arguments

* unit tests: Config.Get can parse and cast values

* unit tests: parse command line args and return string values

* unit test: parameter attribute converter

* merge&parse unit test
2020-10-06 10:19:56 -03:00
Martin-Molinero
79b9009452 ObjectStore delete will delete file (#4816)
- LocalObjectStore.Delete() will also delete file from the local object
  store path if present, this will avoid the issue where restarting the
  object store will re load the same deleted file. Adding unit test.
  Issue https://github.com/QuantConnect/Lean/issues/4811
2020-10-05 20:19:27 -03:00
Gerardo Salazar
e5b5f80d9d Fixes "0" group code value for FixturesAndAppliances (#4820)
* Fixes "0" group code value for FixturesAndAppliances

* Address review: fixes wrong variable name
2020-10-05 20:12:27 -03:00
Stefano Raggi
027fde8f09 Fix IBAutomater restarting after Dispose (#4814)
* Fix IBAutomater restarting after Dispose

* Address review
2020-10-05 12:34:02 -03:00
Martin-Molinero
c7ccd60bf2 Fix unit test stack overflow (#4803)
- PortfolioLooper: implement IDisposable and handle the clean up of
  resources. Remove unused methods.
- BaseResultHandler will reset Console on exit
2020-10-01 14:49:43 -03:00
adam-may
b30bb3fcf5 Bug #4790 - AddBusinessDays ignores holidays when DateTime being compared has time specified (#4791)
Co-authored-by: Adam May <adam.may@lifetrading.com.au>
2020-10-01 09:56:08 -03:00
Gerardo Salazar
40a87eb056 Fixes issue where BidPrice/AskPrice were not adjusted for Quote Ticks (#4789)
* Fixes issue where BidPrice/AskPrice were not adjusted for Quote Ticks

  * Previously, ticks would have their prices (Tick.Value) adjusted whenever
    TickType == TickType.Quote, but would not have their
    BidPrice/AskPrice fields adjusted, thus potentially being orders
    of magnitude such as 4x from the actual Bid/Ask prices.

    This commit applies the pricing scaling factor in a critical
    path where Ticks are adjusted to their scaled price. This issue
    only applied to Resolution.Tick && SecurityType.Equity data.

* Refactors Extensions Tick Scale extension method

  * Adjusts unit test to dispose of resources and assert history count

* Replaces use of FileSystemDataFeed for NullDataFeed in Adjustment test

* Adds regression algorithm testing BidPrice & AskPrice adjustment

* Address review: remove SecurityType check on TickType.Trade adjustments
2020-09-30 19:43:20 -03:00
Jatin Kumar
035b29fdf5 Update readme.md (#4793)
* Update readme.md

American English sadly.
2020-09-30 13:17:59 -07:00
michael-sena
f2fc1aae9e Append the full stacktrace to the algorithm loading exception message. (#4775)
* Append the full stacktrace to the algorithm loading exception message.

* Remove exception message loader duplication

Co-authored-by: Martin Molinero <martin.molinero1@gmail.com>
2020-09-30 12:42:21 -03:00
Martin-Molinero
ff5fc5db5d HistoryRequestFactory Hour ExtendedMarketHours (#4786)
- `HistoryRequestFactory` will not sure extended market hours for hour
  resolution when determining the start time using quantity of bars.
  Adding regression test
2020-09-29 18:16:27 -03:00
Gerardo Salazar
40c3062348 Improves Report Generator stability and fixes various bugs (#4759)
* Improves stability and fixes various bugs

  * Adds unit tests covering changes
  * Adds COVID-19 crisis plots
  * Adjusts styling of crisis plots for more pleasant viewing
  * Fixes bug where null BacktestResult caused application to crash
  * Order JSON bug fixes and stability improvements
  * MaxDrawdownReportElement now produces results for Live
  * Replaced Estimated Capacity w/ Days Live
  * Added Live marker to sharpe ratio
  * Added support for MOO and MOC orders in PortfolioLooper

* Address review: adds new unit tests and cleans up code

  * Bug fix: use LastFillTime instead of Order.Time for MOO and MOC

* Address review: Fixes tests and cleans up code
2020-09-29 10:03:26 -03:00
michael-sena
371f2cd469 Upgrade NLog from 4.4.11 to 4.4.13 (#4772) 2020-09-28 21:52:22 -03:00
Aaron Janeiro Stone
090ceb131e Install instructions: Non-Windows (#4777)
* Added warning for paths in non-Windows

As per https://github.com/dotnet/msbuild/issues/4216

* reserved character instructions - clarity
2020-09-28 21:51:21 -03:00
nazbrok
6885fd130b fix decimal conversion for exponential number (#4750) 2020-09-28 21:50:58 -03:00
Adalyat Nazirov
a450cea8d0 keep actual exchange hours even it different from hours database (#4781) 2020-09-28 20:22:08 -03:00
Stefano Raggi
934128cfa0 Binance Brokerage implementation (#4688)
* Binance Brokerage skeleton

* Market hours

* Implement Symbol Mapper

- known symbols available on /api/v1/exchangeInfo
- fiat currencies are pegged

* Implement GetCashBalance

* Implement GetAccountHoldings

- there are no pre-existing currency swaps
- cash balances are pulled and stored in the cashbook

* Implement GetOpenOrders

* Manage orders: PlaceOrder

* Manage orders: UpdateOrder

Update operation is not supported

* Manage orders: CancelOrder

* Messaging: order book

* Messaging: trades

* Messaging: combine streams

- connect to fake /ws/open channel on init
- case by channel name, but not event type

* Messaging: order depth updates

- ticker symbol is not enough as it pushes updates only once a second, this would be a very incomplete data stream
- fetch ticker snapshot if lastUpdateId == 0
- follow Binance instructions for keeping local orderbook fresh

* Messaging: user data streaming

- Request userDataStream endpoint to get listenKey
- keep listenkey alive
- handle order close event
- handle order fill event

* DataDownloader: get history

- we can aggregate minute candles for higher resolutions

* fix data stream

* Tests: FeeModel tests

* Tests: base brokerage tests

* Tests: download history

* Tests: symbol mapper

* Support StopLimit andd StopMarket orders

* StopMarket orders disabled

Take profit and Stop loss orders are not supported for any symbols (tested with BTCUSDT, ETHUSDT)

* Tests: StopLimit order

* Tests: crypto parsing

* Reissue user data listen key

* comment custom currency limitation

* rework websocket connections

* implement delayed subscription

* adapt ignore message

* add license banner

* use better suited exception type

* avoid message double parsing

* support custom fee values

* extract BinanceApiClient to manage the request/response between lean and binance

* use api events to invoke brokerage events

* do not allow to terminate session if it wasn't allocated.

* update binance exchange info

* tool to add or update binance exchange info

* ExchangeInfo basic test

* Rebase + Resharp

* Binance brokerage updates

- Fix sign bug in sell order fills
- Fix bug in GetHistory
- Remove duplicate symbol from symbol properties db

* Remove unused code

* Revert removal of account currency check

* Update symbols properties database

* Address review

* Address review

- Upgrade API endpoints from v1 to v3
- Updated sub/unsub for new subscription manager
- Subscribe best bid/ask quotes instead of full order book
- Added handling of websocket error messages
- Cleanup + refactor

* Update symbol properties database

* Remove list from symbol mapper

* Fix symbol mapper tests

* Address review

- Fix resubscribe after reconnect
- Fix quote tick edge case

* Fix EnsureCurrencyDataFeed for non-tradeable currencies

* Fix check in EnsureCurrencyDataFeed

* Reuse base class subscribe on reconnect

Co-authored-by: Adalyat Nazirov <aenazirov@gmail.com>
Co-authored-by: Martin-Molinero <martin@quantconnect.com>
2020-09-28 15:57:10 -03:00
Michael Handschuh
c7a74306fb Bug 4731 Fix Option Expiration Order Tags and Order Event IsAssignment Flag (#4743)
* Add OrderRight.GetExerciseDirection(isShort) extension

Returns the OrderDirection resulting from exercise/assignment of a particular
option right

See: BUG #4731

* Fix option exercise/assignment order tags and order event messages

The algorithm manager was doing work to determine whether or not the option ended
in exercise or assignment at expiration. This decision should be left for the exercise
model to decide -- from the algorithm manager's perspective, all that matters is that
the option was expired. The DefaultExerciseModel was updated to properly track whether
the option expired with automatic assignment or exercise, dependending on whether or
not we wrote or bought the option (held liability or right, respectively). Updated unit
tests to check for order event counts and order event messages for option exercise cases.

Fixes: #4731

* Fix typo in algorithm documentation

* Update regression tests order hash

Co-authored-by: Martin Molinero <martin.molinero1@gmail.com>
2020-09-28 15:29:09 -03:00
Aaron Janeiro Stone
cfacc755fa Removal of depreciated module called in test, addition of __init__ (#4769)
* Removal of depreciated module called in test, addition of __init__ to module.

* Delete __init__.py

Currently unnecessary
2020-09-28 11:36:02 -03:00
Michael Handschuh
194ed59cbe Check collection for null (#4763)
In OptionChainUniverseDataCollectionEnumerator.IsValid we check the underlying
foir null and ensure that there are data points but we forget to check the actual
collection object for null, which it is initialized to. The collection remains
null in the case where no data is found.
2020-09-28 10:39:59 -03:00
Martin-Molinero
776caea1d3 Convert a CoarseFundamental data point into a row (#4761)
* Convert a CoarseFundamental data point into a row

- Convert a CoarseFundamental data point into a row. Adding unit tests

* Remove fixed PriceFactor format
2020-09-25 20:31:46 -03:00
Martin-Molinero
460fd626a6 Simplify BaseWebSocketsBrokerage reconnection (#4758)
* Simplify BaseWebSocketsBrokerage reconnection

- Gdax does not emit time pulse so when no subscription was present the
  DefaultConnectionHandler would trigger a reconnect. Replacing for
  directly resubscribing on reconnect.
- TPV == 0 will send warning message instead of being an exception

* Fix unit test
2020-09-25 13:13:33 -03:00
Adalyat Nazirov
1e3a1e3c43 Historical data requests start & time fix (#4733)
* regression tests

* fix: apply the same time convertion to history request time as for data time

* ver2

* fixup

* unit tests

* do not need this conversion because RoundDownInTimeZone returns in proper TZ

* comment

* requested changes

* refactoring

* more refactoring

* fix existing test: should return Sunday if open

* more symbols

* fix existing tests: submit new btcusd data

* fix

* add Cfd symbol
2020-09-24 14:51:35 -03:00
michael-sena
e20725b969 Check both late open and early closes when looking up the next market open/close time (#4755) 2020-09-24 14:51:23 -03:00
Michael Handschuh
151901bbd1 Fix regression *.{lang}.details.logs (#4753)
* Fix regression *.{lang}.details.logs

Regression tests produce syslogs and a details.logs file. The details log file
provides a mechanism for logging data that passes through the result handler,
such as order event data. A change in the initialization ordeer of components in
the engine cause the algorithm id to not be set yet. The base result handler's
AlgorithmId property is populated via the job and is available, so we use that
instance.

* Update RegressionResultHandler.cs

Co-authored-by: Martin-Molinero <martin@quantconnect.com>
2020-09-23 16:35:12 -03:00
Michael Handschuh
fb2f846159 Bug 4722: Prevent Repetitive Factor File Numerical Precision Warnings (#4742)
* Add DataProviderEventArgs base class for IDataProviderEvents event args

This base class includes a Symbol property. This will empower event listeners
to make decisions based on which security (symbol) raised the event. The immediate
use case is preventing multiple numerical precision messages for the same security.
This pattern can equally be applied to other error messages that are raised each
time a security is added to a universe.

See: #BUG-4722

* Update ConcurrentSet.Add to use ISet<T>.Add returning bool

It's a very common pattern to use if (set.Add(item)) which is enabled
via bool ISet<T>.Add(item) but not enabled via void ICollectiont<T>.Add(item).
This change simples changes the default Add implementation to use the ISet<T>
overload and relegates the ICollection<T>.Add implementation to be explicit.

See: #BUG-4722

* Prevent multiple numerical precision messages for same symbol

If a security is continually added/removed from a universe, then the user will
see this message each time the security is added. This results in some spam.
This change simply remembers for which symbols we've notified the user about the
numerical precision issue.

Fixes: #BUG-4722
2020-09-22 21:06:59 -03:00
Martin-Molinero
d03ac0fd90 Gdax reconnection & Bitfinex minor fixes (#4748)
* Fix for BaseWebSocketsBrokerage reconnection

- `BaseWebsocketsBrokerage` will handle reconnection using the
existing `DefaultConnectionHandler` to avoid code duplication.

* Fix for Bitfinex failed Subscription calls

* Fix for bitfinex orderbook

* Fix unit test
2020-09-22 20:47:41 -03:00
923 changed files with 50377 additions and 65133 deletions

3
.gitignore vendored
View File

@@ -271,3 +271,6 @@ QuantConnect.Lean.sln.DotSettings*
#User notebook files
Research/Notebooks
#Docker result files
Results/

18
.idea/Lean.iml generated
View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/Algorithm.Python" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/Algorithm.Python/stubs" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
<component name="TestRunnerService">
<option name="PROJECT_TEST_RUNNER" value="pytest" />
</component>
</module>

14
.idea/readme.md generated
View File

@@ -91,14 +91,14 @@ From a terminal; Pycharm has a built in terminal on the bottom taskbar labeled *
2. Using the **run_docker.cfg** to store args for repeated use; any blank entries will resort to default values! example: **_./run_docker.bat run_docker.cfg_**
image=quantconnect/lean:latest
config_file=
data_dir=
results_dir=
debugging=
python_dir=
IMAGE=quantconnect/lean:latest
CONFIG_FILE=
DATA_DIR=
RESULTS_DIR=
DEBUGGING=
PYTHON_DIR=
3. Inline arguments; anything you don't enter will use the default args! example: **_./run_docker.bat debugging=y_**
3. Inline arguments; anything you don't enter will use the default args! example: **_./run_docker.bat DEBUGGING=y_**
* Accepted args for inline include all listed in the file in #2; must follow the **key=value** format
<br />

View File

@@ -16,6 +16,7 @@ before_install:
- conda install -y cython=0.29.15
- conda install -y scipy=1.4.1
- conda install -y wrapt=1.12.1
- pip install pyarrow==1.0.1
install:
- nuget restore QuantConnect.Lean.sln
- nuget install NUnit.Runners -Version 3.11.1 -OutputDirectory testrunner
@@ -23,4 +24,4 @@ script:
- msbuild /p:Configuration=Release /p:VbcToolExe=vbnc.exe QuantConnect.Lean.sln
- mono ./testrunner/NUnit.ConsoleRunner.3.11.1/tools/nunit3-console.exe ./Tests/bin/Release/QuantConnect.Tests.dll --where "cat != TravisExclude" --labels=Off
- chmod +x ci_build_stubs.sh
- sudo -E ./ci_build_stubs.sh -ipy -g -p
- sudo -E ./ci_build_stubs.sh -d -t -g -p

View File

@@ -106,14 +106,14 @@ From a terminal launch the run_docker.bat/.sh script; there are a few choices on
2. Using the **run_docker.cfg** to store args for repeated use; any blank entries will resort to default values! example: **_./run_docker.bat run_docker.cfg_**
image=quantconnect/lean:latest
config_file=
data_dir=
results_dir=
debugging=
python_dir=
IMAGE=quantconnect/lean:latest
CONFIG_FILE=
DATA_DIR=
RESULTS_DIR=
DEBUGGING=
PYTHON_DIR=
3. Inline arguments; anything you don't enter will use the default args! example: **_./run_docker.bat debugging=y_**
3. Inline arguments; anything you don't enter will use the default args! example: **_./run_docker.bat DEBUGGING=y_**
* Accepted args for inline include all listed in the file in #2
<br />

34
.vscode/readme.md vendored
View File

@@ -101,14 +101,14 @@ This section will cover how to actually launch Lean in the container with your d
<h3>Option 1 (Recommended)</h3>
In VS Code click on the debug/run icon on the left toolbar, at the top you should see a drop down menu with launch options, be sure to select **Debug in Container**. This option will kick off a launch script that will start the docker. With this specific launch option the parameters are already configured in VS Codes **tasks.json** under the **run-docker** task args. These set arguements are:
In VS Code click on the debug/run icon on the left toolbar, at the top you should see a drop down menu with launch options, be sure to select **Debug in Container**. This option will kick off a launch script that will start the docker. With this specific launch option the parameters are already configured in VS Codes **tasks.json** under the **run-docker** task args. These set arguments are:
"image=quantconnect/lean:latest",
"config_file=${workspaceFolder}/Launcher/config.json",
"data_dir=${workspaceFolder}/Data",
"results_dir=${workspaceFolder}/",
"debugging=Y",
"python_location=${workspaceFolder}/Algorithm.Python"
"IMAGE=quantconnect/lean:latest",
"CONFIG_FILE=${workspaceFolder}/Launcher/config.json",
"DATA_DIR=${workspaceFolder}/Data",
"RESULTS_DIR=${workspaceFolder}/Results",
"DEBUGGING=Y",
"PYHTON_DIR=${workspaceFolder}/Algorithm.Python"
As defaults these are all great! Feel free to change them as needed for your setup.
@@ -120,21 +120,21 @@ From a terminal launch the run_docker.bat/.sh script; there are a few choices on
1. Launch with no parameters and answer the questions regarding configuration (Press enter for defaults)
*   Enter docker image [default: quantconnect/lean:latest]:
*   Enter absolute path to Lean config file [default: _~currentDir_\Launcher\config.json]:
*   Enter absolute path to Data folder [default: ~_currentDir_\Data\]:
*   Enter absolute path to store results [default: ~_currentDir_\]:
*   Enter absolute path to Lean config file [default: .\Launcher\config.json]:
*   Enter absolute path to Data folder [default: .\Data\]:
*   Enter absolute path to store results [default: .\Results]:
* Would you like to debug C#? (Requires mono debugger attachment) [default: N]:
2. Using the **run_docker.cfg** to store args for repeated use; any blank entries will resort to default values! example: **_./run_docker.bat run_docker.cfg_**
image=quantconnect/lean:latest
config_file=
data_dir=
results_dir=
debugging=
python_dir=
IMAGE=quantconnect/lean:latest
CONFIG_FILE=
DATA_DIR=
RESULTS_DIR=
DEBUGGING=
PYTHON_DIR=
3. Inline arguments; anything you don't enter will use the default args! example: **_./run_docker.bat debugging=y_**
3. Inline arguments; anything you don't enter will use the default args! example: **_./run_docker.bat DEBUGGING=y_**
* Accepted args for inline include all listed in the file in #2
<br />

View File

@@ -1,5 +0,0 @@
{
"python.autoComplete.extraPaths": [
"Algorithm.Python/stubs"
]
}

14
.vscode/tasks.json vendored
View File

@@ -51,13 +51,13 @@
"command": "${workspaceFolder}/run_docker.sh"
},
"args": [
"image=quantconnect/lean:latest",
"config_file=${workspaceFolder}/Launcher/config.json",
"data_dir=${workspaceFolder}/Data",
"results_dir=${workspaceFolder}/",
"debugging=Y",
"python_dir=${workspaceFolder}/Algorithm.Python",
"exit=Y"
"IMAGE=quantconnect/lean:latest",
"CONFIG_FILE=${workspaceFolder}/Launcher/config.json",
"DATA_DIR=${workspaceFolder}/Data",
"RESULTS_DIR=${workspaceFolder}/Results",
"DEBUGGING=Y",
"PYTHON_DIR=${workspaceFolder}/Algorithm.Python",
"EXIT=Y"
],
"problemMatcher": [
{

View File

@@ -0,0 +1,210 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This regression algorithm tests that we receive the expected data when
/// we add future option contracts individually using <see cref="AddFutureOptionContract"/>
/// </summary>
public class AddFutureOptionContractDataStreamingRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private bool _onDataReached;
private bool _invested;
private Symbol _es20h20;
private Symbol _es19m20;
private readonly HashSet<Symbol> _symbolsReceived = new HashSet<Symbol>();
private readonly HashSet<Symbol> _expectedSymbolsReceived = new HashSet<Symbol>();
private readonly Dictionary<Symbol, List<QuoteBar>> _dataReceived = new Dictionary<Symbol, List<QuoteBar>>();
public override void Initialize()
{
SetStartDate(2020, 1, 5);
SetEndDate(2020, 1, 6);
_es20h20 = AddFutureContract(
QuantConnect.Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, new DateTime(2020, 3, 20)),
Resolution.Minute).Symbol;
_es19m20 = AddFutureContract(
QuantConnect.Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, new DateTime(2020, 6, 19)),
Resolution.Minute).Symbol;
var optionChains = OptionChainProvider.GetOptionContractList(_es20h20, Time)
.Concat(OptionChainProvider.GetOptionContractList(_es19m20, Time));
foreach (var optionContract in optionChains)
{
_expectedSymbolsReceived.Add(AddFutureOptionContract(optionContract, Resolution.Minute).Symbol);
}
if (_expectedSymbolsReceived.Count == 0)
{
throw new InvalidOperationException("Expected Symbols receive count is 0, expected >0");
}
}
public override void OnData(Slice data)
{
if (!data.HasData)
{
return;
}
_onDataReached = true;
var hasOptionQuoteBars = false;
foreach (var qb in data.QuoteBars.Values)
{
if (qb.Symbol.SecurityType != SecurityType.FutureOption)
{
continue;
}
hasOptionQuoteBars = true;
_symbolsReceived.Add(qb.Symbol);
if (!_dataReceived.ContainsKey(qb.Symbol))
{
_dataReceived[qb.Symbol] = new List<QuoteBar>();
}
_dataReceived[qb.Symbol].Add(qb);
}
if (_invested || !hasOptionQuoteBars)
{
return;
}
if (data.ContainsKey(_es20h20) && data.ContainsKey(_es19m20))
{
SetHoldings(_es20h20, 0.2);
SetHoldings(_es19m20, 0.2);
_invested = true;
}
}
public override void OnEndOfAlgorithm()
{
base.OnEndOfAlgorithm();
if (!_onDataReached)
{
throw new Exception("OnData() was never called.");
}
if (_symbolsReceived.Count != _expectedSymbolsReceived.Count)
{
throw new AggregateException($"Expected {_expectedSymbolsReceived.Count} option contracts Symbols, found {_symbolsReceived.Count}");
}
var missingSymbols = new List<Symbol>();
foreach (var expectedSymbol in _expectedSymbolsReceived)
{
if (!_symbolsReceived.Contains(expectedSymbol))
{
missingSymbols.Add(expectedSymbol);
}
}
if (missingSymbols.Count > 0)
{
throw new Exception($"Symbols: \"{string.Join(", ", missingSymbols)}\" were not found in OnData");
}
foreach (var expectedSymbol in _expectedSymbolsReceived)
{
var data = _dataReceived[expectedSymbol];
var nonDupeDataCount = data.Select(x =>
{
x.EndTime = default(DateTime);
return x;
}).Distinct().Count();
if (nonDupeDataCount < 1000)
{
throw new Exception($"Received too few data points. Expected >=1000, found {nonDupeDataCount} for {expectedSymbol}");
}
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "2"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "217.585%"},
{"Drawdown", "0.600%"},
{"Expectancy", "0"},
{"Net Profit", "0.635%"},
{"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", "-14.395"},
{"Tracking Error", "0.043"},
{"Treynor Ratio", "0"},
{"Total Fees", "$7.40"},
{"Fitness Score", "1"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
{"Portfolio Turnover", "3.199"},
{"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", "1074366800"}
};
}
}

View File

@@ -0,0 +1,244 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
using QuantConnect.Securities.Future;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This regression algorithm tests that we only receive the option chain for a single future contract
/// in the option universe filter.
/// </summary>
public class AddFutureOptionSingleOptionChainSelectedInUniverseFilterRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private bool _invested;
private bool _onDataReached;
private bool _optionFilterRan;
private readonly HashSet<Symbol> _symbolsReceived = new HashSet<Symbol>();
private readonly HashSet<Symbol> _expectedSymbolsReceived = new HashSet<Symbol>();
private readonly Dictionary<Symbol, List<QuoteBar>> _dataReceived = new Dictionary<Symbol, List<QuoteBar>>();
private Future _es;
public override void Initialize()
{
SetStartDate(2020, 1, 5);
SetEndDate(2020, 1, 6);
_es = AddFuture(Futures.Indices.SP500EMini, Resolution.Minute, Market.CME);
_es.SetFilter((futureFilter) =>
{
return futureFilter.Expiration(0, 365).ExpirationCycle(new[] { 3, 6 });
});
AddFutureOption(_es.Symbol, optionContracts =>
{
_optionFilterRan = true;
var expiry = new HashSet<DateTime>(optionContracts.Select(x => x.Underlying.ID.Date)).SingleOrDefault();
// Cast to IEnumerable<Symbol> because OptionFilterContract overrides some LINQ operators like `Select` and `Where`
// and cause it to mutate the underlying Symbol collection when using those operators.
var symbol = new HashSet<Symbol>(((IEnumerable<Symbol>)optionContracts).Select(x => x.Underlying)).SingleOrDefault();
if (expiry == null || symbol == null)
{
throw new InvalidOperationException("Expected a single Option contract in the chain, found 0 contracts");
}
var enumerator = optionContracts.GetEnumerator();
while (enumerator.MoveNext())
{
_expectedSymbolsReceived.Add(enumerator.Current);
}
return optionContracts;
});
}
public override void OnData(Slice data)
{
if (!data.HasData)
{
return;
}
_onDataReached = true;
var hasOptionQuoteBars = false;
foreach (var qb in data.QuoteBars.Values)
{
if (qb.Symbol.SecurityType != SecurityType.FutureOption)
{
continue;
}
hasOptionQuoteBars = true;
_symbolsReceived.Add(qb.Symbol);
if (!_dataReceived.ContainsKey(qb.Symbol))
{
_dataReceived[qb.Symbol] = new List<QuoteBar>();
}
_dataReceived[qb.Symbol].Add(qb);
}
if (_invested || !hasOptionQuoteBars)
{
return;
}
foreach (var chain in data.OptionChains.Values)
{
var futureInvested = false;
var optionInvested = false;
foreach (var option in chain.Contracts.Keys)
{
if (futureInvested && optionInvested)
{
return;
}
var future = option.Underlying;
if (!optionInvested && data.ContainsKey(option))
{
MarketOrder(option, 1);
_invested = true;
optionInvested = true;
}
if (!futureInvested && data.ContainsKey(future))
{
MarketOrder(future, 1);
_invested = true;
futureInvested = true;
}
}
}
}
public override void OnEndOfAlgorithm()
{
base.OnEndOfAlgorithm();
if (!_optionFilterRan)
{
throw new InvalidOperationException("Option chain filter was never ran");
}
if (!_onDataReached)
{
throw new Exception("OnData() was never called.");
}
if (_symbolsReceived.Count != _expectedSymbolsReceived.Count)
{
throw new AggregateException($"Expected {_expectedSymbolsReceived.Count} option contracts Symbols, found {_symbolsReceived.Count}");
}
var missingSymbols = new List<Symbol>();
foreach (var expectedSymbol in _expectedSymbolsReceived)
{
if (!_symbolsReceived.Contains(expectedSymbol))
{
missingSymbols.Add(expectedSymbol);
}
}
if (missingSymbols.Count > 0)
{
throw new Exception($"Symbols: \"{string.Join(", ", missingSymbols)}\" were not found in OnData");
}
foreach (var expectedSymbol in _expectedSymbolsReceived)
{
var data = _dataReceived[expectedSymbol];
var nonDupeDataCount = data.Select(x =>
{
x.EndTime = default(DateTime);
return x;
}).Distinct().Count();
if (nonDupeDataCount < 1000)
{
throw new Exception($"Received too few data points. Expected >=1000, found {nonDupeDataCount} for {expectedSymbol}");
}
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "2"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "-15.625%"},
{"Drawdown", "0.200%"},
{"Expectancy", "0"},
{"Net Profit", "-0.093%"},
{"Sharpe Ratio", "-11.181"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0.002"},
{"Beta", "-0.016"},
{"Annual Standard Deviation", "0.001"},
{"Annual Variance", "0"},
{"Information Ratio", "-14.343"},
{"Tracking Error", "0.044"},
{"Treynor Ratio", "0.479"},
{"Total Fees", "$3.70"},
{"Fitness Score", "0.41"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "-185.654"},
{"Portfolio Turnover", "0.821"},
{"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", "1532330301"}
};
}
}

View File

@@ -0,0 +1,164 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using System.Collections.Generic;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// We add an option contract using <see cref="QCAlgorithm.AddOptionContract"/> and place a trade and wait till it expires
/// later will liquidate the resulting equity position and assert both option and underlying get removed
/// </summary>
public class AddOptionContractExpiresRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private DateTime _expiration = new DateTime(2014, 06, 21);
private Symbol _option;
private Symbol _twx;
private bool _traded;
public override void Initialize()
{
SetStartDate(2014, 06, 05);
SetEndDate(2014, 06, 30);
_twx = QuantConnect.Symbol.Create("TWX", SecurityType.Equity, Market.USA);
AddUniverse("my-daily-universe-name", time => new List<string> { "AAPL" });
}
public override void OnData(Slice data)
{
if (_option == null)
{
var option = OptionChainProvider.GetOptionContractList(_twx, Time)
.OrderBy(symbol => symbol.ID.Symbol)
.FirstOrDefault(optionContract => optionContract.ID.Date == _expiration
&& optionContract.ID.OptionRight == OptionRight.Call
&& optionContract.ID.OptionStyle == OptionStyle.American);
if (option != null)
{
_option = AddOptionContract(option).Symbol;
}
}
if (_option != null && Securities[_option].Price != 0 && !_traded)
{
_traded = true;
Buy(_option, 1);
foreach (var symbol in new [] { _option, _option.Underlying })
{
var config = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(symbol).ToList();
if (!config.Any())
{
throw new Exception($"Was expecting configurations for {symbol}");
}
if (config.Any(dataConfig => dataConfig.DataNormalizationMode != DataNormalizationMode.Raw))
{
throw new Exception($"Was expecting DataNormalizationMode.Raw configurations for {symbol}");
}
}
}
if (Time.Date > _expiration)
{
if (SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(_option).Any())
{
throw new Exception($"Unexpected configurations for {_option} after it has been delisted");
}
if (Securities[_twx].Invested)
{
if (!SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(_twx).Any())
{
throw new Exception($"Was expecting configurations for {_twx}");
}
// first we liquidate the option exercised position
Liquidate(_twx);
}
}
else if (Time.Date > _expiration && !Securities[_twx].Invested)
{
if (SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(_twx).Any())
{
throw new Exception($"Unexpected configurations for {_twx} after it has been liquidated");
}
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "3"},
{"Average Win", "2.73%"},
{"Average Loss", "-2.98%"},
{"Compounding Annual Return", "-4.619%"},
{"Drawdown", "0.300%"},
{"Expectancy", "-0.042"},
{"Net Profit", "-0.332%"},
{"Sharpe Ratio", "-3.7"},
{"Probabilistic Sharpe Ratio", "0.563%"},
{"Loss Rate", "50%"},
{"Win Rate", "50%"},
{"Profit-Loss Ratio", "0.92"},
{"Alpha", "-0.023"},
{"Beta", "0.005"},
{"Annual Standard Deviation", "0.006"},
{"Annual Variance", "0"},
{"Information Ratio", "-3.424"},
{"Tracking Error", "0.057"},
{"Treynor Ratio", "-4.775"},
{"Total Fees", "$2.00"},
{"Fitness Score", "0"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "-43.418"},
{"Return Over Maximum Drawdown", "-14.274"},
{"Portfolio Turnover", "0.007"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
{"Long Insight Count", "0"},
{"Short Insight Count", "0"},
{"Long/Short Ratio", "100%"},
{"Estimated Monthly Alpha Value", "$0"},
{"Total Accumulated Estimated Alpha Value", "$0"},
{"Mean Population Estimated Insight Value", "$0"},
{"Mean Population Direction", "0%"},
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "-1185639451"}
};
}
}

View File

@@ -0,0 +1,216 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Interfaces;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// We add an option contract using <see cref="QCAlgorithm.AddOptionContract"/> and place a trade, the underlying
/// gets deselected from the universe selection but should still be present since we manually added the option contract.
/// Later we call <see cref="QCAlgorithm.RemoveOptionContract"/> and expect both option and underlying to be removed.
/// </summary>
public class AddOptionContractFromUniverseRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private DateTime _expiration = new DateTime(2014, 06, 21);
private SecurityChanges _securityChanges = SecurityChanges.None;
private Symbol _option;
private Symbol _aapl;
private Symbol _twx;
private bool _traded;
public override void Initialize()
{
_twx = QuantConnect.Symbol.Create("TWX", SecurityType.Equity, Market.USA);
_aapl = QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA);
UniverseSettings.Resolution = Resolution.Minute;
UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw;
SetStartDate(2014, 06, 05);
SetEndDate(2014, 06, 09);
AddUniverse(enumerable => new[] { Time.Date <= new DateTime(2014, 6, 5) ? _twx : _aapl },
enumerable => new[] { Time.Date <= new DateTime(2014, 6, 5) ? _twx : _aapl });
}
public override void OnData(Slice data)
{
if (_option != null && Securities[_option].Price != 0 && !_traded)
{
_traded = true;
Buy(_option, 1);
}
if (Time.Date > new DateTime(2014, 6, 5))
{
if (Time < new DateTime(2014, 6, 6, 14, 0, 0))
{
var configs = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(_twx);
// assert underlying still there after the universe selection removed it, still used by the manually added option contract
if (!configs.Any())
{
throw new Exception($"Was expecting configurations for {_twx}" +
$" even after it has been deselected from coarse universe because we still have the option contract.");
}
}
else if (Time == new DateTime(2014, 6, 6, 14, 0, 0))
{
// liquidate & remove the option
RemoveOptionContract(_option);
}
// assert underlying was finally removed
else if(Time > new DateTime(2014, 6, 6, 14, 0, 0))
{
foreach (var symbol in new[] { _option, _option.Underlying })
{
var configs = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(symbol);
if (configs.Any())
{
throw new Exception($"Unexpected configuration for {symbol} after it has been deselected from coarse universe and option contract is removed.");
}
}
}
}
}
public override void OnSecuritiesChanged(SecurityChanges changes)
{
if (_securityChanges.RemovedSecurities.Intersect(changes.RemovedSecurities).Any())
{
throw new Exception($"SecurityChanges.RemovedSecurities intersect {changes.RemovedSecurities}. We expect no duplicate!");
}
if (_securityChanges.AddedSecurities.Intersect(changes.AddedSecurities).Any())
{
throw new Exception($"SecurityChanges.AddedSecurities intersect {changes.RemovedSecurities}. We expect no duplicate!");
}
// keep track of all removed and added securities
_securityChanges += changes;
if (changes.AddedSecurities.Any(security => security.Symbol.SecurityType == SecurityType.Option))
{
return;
}
foreach (var addedSecurity in changes.AddedSecurities)
{
var option = OptionChainProvider.GetOptionContractList(addedSecurity.Symbol, Time)
.OrderBy(symbol => symbol.ID.Symbol)
.First(optionContract => optionContract.ID.Date == _expiration
&& optionContract.ID.OptionRight == OptionRight.Call
&& optionContract.ID.OptionStyle == OptionStyle.American);
AddOptionContract(option);
foreach (var symbol in new[] { option, option.Underlying })
{
var config = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(symbol).ToList();
if (!config.Any())
{
throw new Exception($"Was expecting configurations for {symbol}");
}
if (config.Any(dataConfig => dataConfig.DataNormalizationMode != DataNormalizationMode.Raw))
{
throw new Exception($"Was expecting DataNormalizationMode.Raw configurations for {symbol}");
}
}
// just keep the first we got
if (_option == null)
{
_option = option;
}
}
}
public override void OnEndOfAlgorithm()
{
if (SubscriptionManager.Subscriptions.Any(dataConfig => dataConfig.Symbol == _twx || dataConfig.Symbol.Underlying == _twx))
{
throw new Exception($"Was NOT expecting any configurations for {_twx} or it's options, since we removed the contract");
}
if (SubscriptionManager.Subscriptions.All(dataConfig => dataConfig.Symbol != _aapl))
{
throw new Exception($"Was expecting configurations for {_aapl}");
}
if (SubscriptionManager.Subscriptions.All(dataConfig => dataConfig.Symbol.Underlying != _aapl))
{
throw new Exception($"Was expecting options configurations for {_aapl}");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "2"},
{"Average Win", "0%"},
{"Average Loss", "-0.23%"},
{"Compounding Annual Return", "-15.596%"},
{"Drawdown", "0.200%"},
{"Expectancy", "-1"},
{"Net Profit", "-0.232%"},
{"Sharpe Ratio", "-7.739"},
{"Probabilistic Sharpe Ratio", "1.216%"},
{"Loss Rate", "100%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0.027"},
{"Beta", "-0.174"},
{"Annual Standard Deviation", "0.006"},
{"Annual Variance", "0"},
{"Information Ratio", "-11.586"},
{"Tracking Error", "0.042"},
{"Treynor Ratio", "0.286"},
{"Total Fees", "$2.00"},
{"Fitness Score", "0"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "-19.883"},
{"Return Over Maximum Drawdown", "-67.224"},
{"Portfolio Turnover", "0.014"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
{"Long Insight Count", "0"},
{"Short Insight Count", "0"},
{"Long/Short Ratio", "100%"},
{"Estimated Monthly Alpha Value", "$0"},
{"Total Accumulated Estimated Alpha Value", "$0"},
{"Mean Population Estimated Insight Value", "$0"},
{"Mean Population Direction", "0%"},
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "721476625"}
};
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Custom.Quiver;
namespace QuantConnect.Algorithm.CSharp.AltData
{
/// <summary>
/// Quiver Quantitative is a provider of alternative data.
/// This algorithm shows how to consume the <see cref="QuiverWallStreetBets"/>
/// </summary>
public class QuiverWallStreetBetsDataAlgorithm : QCAlgorithm
{
/// <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(2019, 1, 1);
SetEndDate(2020, 6, 1);
SetCash(100000);
var aapl = AddEquity("AAPL", Resolution.Daily).Symbol;
var quiverWSBSymbol = AddData<QuiverWallStreetBets>(aapl).Symbol;
var history = History<QuiverWallStreetBets>(quiverWSBSymbol, 60, Resolution.Daily);
Debug($"We got {history.Count()} items from our history request");
}
public override void OnData(Slice data)
{
var points = data.Get<QuiverWallStreetBets>();
foreach (var point in points.Values)
{
// Go long in the stock if it was mentioned more than 5 times in the WallStreetBets daily discussion
if (point.Mentions > 5)
{
SetHoldings(point.Symbol.Underlying, 1);
}
// Go short in the stock if it was mentioned less than 5 times in the WallStreetBets daily discussion
if (point.Mentions < 5)
{
SetHoldings(point.Symbol.Underlying, -1);
}
}
}
}
}

View File

@@ -75,7 +75,7 @@ namespace QuantConnect.Algorithm.CSharp
/// </summary>
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
public override void OnData(Slice data)
{
{
if (!_equityBought && data.ContainsKey(_spy)) {
//Buy our Equity
var quantity = CalculateOrderQuantity(_spy, .1m);
@@ -114,7 +114,7 @@ namespace QuantConnect.Algorithm.CSharp
/// </summary>
/// <param name="orderEvent">OrderEvent object that contains all the information about the event</param>
public override void OnOrderEvent(OrderEvent orderEvent)
{
{
// Get the order from our transactions
var order = Transactions.GetOrderById(orderEvent.OrderId);
@@ -147,7 +147,7 @@ namespace QuantConnect.Algorithm.CSharp
// All PartiallyFilled orders should have a LastFillTime
case OrderStatus.PartiallyFilled:
if (order.LastFillTime == null)
if (order.LastFillTime == null)
{
throw new Exception("LastFillTime should not be null");
}
@@ -183,9 +183,9 @@ namespace QuantConnect.Algorithm.CSharp
throw new Exception("OptionExercise order price should be strike price!!");
}
if (orderEvent.Quantity != 1)
if (orderEvent.Quantity != -1)
{
throw new Exception("OrderEvent Quantity should be 1");
throw new Exception("OrderEvent Quantity should be -1");
}
}
@@ -317,7 +317,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Treynor Ratio", "-0.018"},
{"Total Fees", "$2.00"},
{"Fitness Score", "0.213"},
{"OrderListHash", "-1514011542"}
{"OrderListHash", "904167951"}
};
}
}

View File

@@ -107,7 +107,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "498372354"}
{"OrderListHash", "-1575550889"}
};
}
}

View File

@@ -119,4 +119,4 @@ namespace QuantConnect.Algorithm.CSharp
{"OrderListHash", "491919591"}
};
}
}
}

View File

@@ -222,12 +222,12 @@ namespace QuantConnect.Algorithm.CSharp
{"Information Ratio", "0"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$85.33"},
{"Total Fees", "$85.34"},
{"Fitness Score", "0.5"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "-43.937"},
{"Return Over Maximum Drawdown", "-43.943"},
{"Portfolio Turnover", "1.028"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
@@ -242,7 +242,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "1073240275"}
{"OrderListHash", "956597072"}
};
}
}

View File

@@ -141,7 +141,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "1935621950"}
{"OrderListHash", "687310345"}
};
}
}

View File

@@ -151,8 +151,8 @@ namespace QuantConnect.Algorithm.CSharp
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "-30.28"},
{"Portfolio Turnover", "1.029"},
{"Return Over Maximum Drawdown", "-30.158"},
{"Portfolio Turnover", "1.033"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
@@ -166,7 +166,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "-1708974186"}
{"OrderListHash", "1349023435"}
};
}
}

View File

@@ -0,0 +1,189 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Interfaces;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Demonstration of how to chain a coarse and fine universe selection with an option chain universe selection model
/// that will add and remove an <see cref="OptionChainUniverse"/> for each symbol selected on fine
/// </summary>
public class CoarseFineOptionUniverseChainRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
// initialize our changes to nothing
private SecurityChanges _changes = SecurityChanges.None;
private int _optionCount;
private Symbol _lastEquityAdded;
private Symbol _aapl;
private Symbol _twx;
public override void Initialize()
{
_twx = QuantConnect.Symbol.Create("TWX", SecurityType.Equity, Market.USA);
_aapl = QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA);
UniverseSettings.Resolution = Resolution.Minute;
SetStartDate(2014, 06, 05);
SetEndDate(2014, 06, 06);
var selectionUniverse = AddUniverse(enumerable => new[] { Time.Date <= new DateTime(2014, 6, 5) ? _twx : _aapl },
enumerable => new[] { Time.Date <= new DateTime(2014, 6, 5) ? _twx : _aapl });
AddUniverseOptions(selectionUniverse, universe =>
{
if (universe.Underlying == null)
{
throw new Exception("Underlying data point is null! This shouldn't happen, each OptionChainUniverse handles and should provide this");
}
return universe.IncludeWeeklys()
.FrontMonth()
.Contracts(universe.Take(5));
});
}
public override void OnData(Slice data)
{
// if we have no changes, do nothing
if (_changes == SecurityChanges.None ||
_changes.AddedSecurities.Any(security => security.Price == 0))
{
return;
}
// liquidate removed securities
foreach (var security in _changes.RemovedSecurities)
{
if (security.Invested)
{
Liquidate(security.Symbol);
}
}
foreach (var security in _changes.AddedSecurities)
{
if (!security.Symbol.HasUnderlying)
{
_lastEquityAdded = security.Symbol;
}
else
{
// options added should all match prev added security
if (security.Symbol.Underlying != _lastEquityAdded)
{
throw new Exception($"Unexpected symbol added {security.Symbol}");
}
_optionCount++;
}
SetHoldings(security.Symbol, 0.05m);
var config = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(security.Symbol).ToList();
if (!config.Any())
{
throw new Exception($"Was expecting configurations for {security.Symbol}");
}
if (config.Any(dataConfig => dataConfig.DataNormalizationMode != DataNormalizationMode.Raw))
{
throw new Exception($"Was expecting DataNormalizationMode.Raw configurations for {security.Symbol}");
}
}
_changes = SecurityChanges.None;
}
public override void OnSecuritiesChanged(SecurityChanges changes)
{
_changes += changes;
}
public override void OnEndOfAlgorithm()
{
var config = SubscriptionManager.Subscriptions.ToList();
if (config.Any(dataConfig => dataConfig.Symbol == _twx || dataConfig.Symbol.Underlying == _twx))
{
throw new Exception($"Was NOT expecting any configurations for {_twx} or it's options, since coarse/fine should have deselected it");
}
if (_optionCount == 0)
{
throw new Exception("Option universe chain did not add any option!");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "13"},
{"Average Win", "0.65%"},
{"Average Loss", "-0.05%"},
{"Compounding Annual Return", "3216040423556140000000000%"},
{"Drawdown", "0.500%"},
{"Expectancy", "1.393"},
{"Net Profit", "32.840%"},
{"Sharpe Ratio", "7.14272222483913E+15"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "83%"},
{"Win Rate", "17%"},
{"Profit-Loss Ratio", "13.36"},
{"Alpha", "2.59468989671647E+16"},
{"Beta", "67.661"},
{"Annual Standard Deviation", "3.633"},
{"Annual Variance", "13.196"},
{"Information Ratio", "7.24987266907741E+15"},
{"Tracking Error", "3.579"},
{"Treynor Ratio", "383485597312030"},
{"Total Fees", "$13.00"},
{"Fitness Score", "0.232"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
{"Portfolio Turnover", "0.232"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
{"Long Insight Count", "0"},
{"Short Insight Count", "0"},
{"Long/Short Ratio", "100%"},
{"Estimated Monthly Alpha Value", "$0"},
{"Total Accumulated Estimated Alpha Value", "$0"},
{"Mean Population Estimated Insight Value", "$0"},
{"Mean Population Direction", "0%"},
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "1630141557"}
};
}
}

View File

@@ -0,0 +1,138 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
using System.Collections.Generic;
using QuantConnect.Data;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Demonstration of using custom buying power model in backtesting.
/// QuantConnect allows you to model all orders as deeply and accurately as you need.
/// </summary>
/// <meta name="tag" content="trading and orders" />
/// <meta name="tag" content="transaction fees and slippage" />
/// <meta name="tag" content="custom buying power models" />
public class CustomBuyingPowerModelAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol _spy;
public override void Initialize()
{
SetStartDate(2013, 10, 01);
SetEndDate(2013, 10, 31);
var security = AddEquity("SPY", Resolution.Hour);
_spy = security.Symbol;
// set the buying power model
security.SetBuyingPowerModel(new CustomBuyingPowerModel());
}
public void OnData(Slice slice)
{
if (Portfolio.Invested)
{
return;
}
var quantity = CalculateOrderQuantity(_spy, 1m);
if (quantity % 100 != 0)
{
throw new Exception($"CustomBuyingPowerModel only allow quantity that is multiple of 100 and {quantity} was found");
}
// We normally get insufficient buying power model, but the
// CustomBuyingPowerModel always says that there is sufficient buying power for the orders
MarketOrder(_spy, quantity * 10);
}
public class CustomBuyingPowerModel : BuyingPowerModel
{
public override GetMaximumOrderQuantityResult GetMaximumOrderQuantityForTargetBuyingPower(
GetMaximumOrderQuantityForTargetBuyingPowerParameters parameters)
{
var quantity = base.GetMaximumOrderQuantityForTargetBuyingPower(parameters).Quantity;
quantity = Math.Floor(quantity / 100) * 100;
return new GetMaximumOrderQuantityResult(quantity);
}
public override HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder(
HasSufficientBuyingPowerForOrderParameters parameters)
{
return new HasSufficientBuyingPowerForOrderResult(true);
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "1"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "5672.520%"},
{"Drawdown", "22.500%"},
{"Expectancy", "0"},
{"Net Profit", "40.601%"},
{"Sharpe Ratio", "40.201"},
{"Probabilistic Sharpe Ratio", "77.339%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "41.848"},
{"Beta", "9.224"},
{"Annual Standard Deviation", "1.164"},
{"Annual Variance", "1.355"},
{"Information Ratio", "44.459"},
{"Tracking Error", "1.04"},
{"Treynor Ratio", "5.073"},
{"Total Fees", "$30.00"},
{"Fitness Score", "0.418"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "113.05"},
{"Return Over Maximum Drawdown", "442.81"},
{"Portfolio Turnover", "0.418"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
{"Long Insight Count", "0"},
{"Short Insight Count", "0"},
{"Long/Short Ratio", "100%"},
{"Estimated Monthly Alpha Value", "$0"},
{"Total Accumulated Estimated Alpha Value", "$0"},
{"Mean Population Estimated Insight Value", "$0"},
{"Mean Population Direction", "0%"},
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "639761089"}
};
}
}

View File

@@ -26,11 +26,12 @@ using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Demonstration of using custom fee, slippage and fill models for modelling transactions in backtesting.
/// Demonstration of using custom fee, slippage, fill, and buying power models for modelling transactions in backtesting.
/// QuantConnect allows you to model all orders as deeply and accurately as you need.
/// </summary>
/// <meta name="tag" content="trading and orders" />
/// <meta name="tag" content="transaction fees and slippage" />
/// <meta name="tag" content="custom buying power models" />
/// <meta name="tag" content="custom transaction models" />
/// <meta name="tag" content="custom slippage models" />
/// <meta name="tag" content="custom fee models" />
@@ -50,6 +51,7 @@ namespace QuantConnect.Algorithm.CSharp
_security.SetFeeModel(new CustomFeeModel(this));
_security.SetFillModel(new CustomFillModel(this));
_security.SetSlippageModel(new CustomSlippageModel(this));
_security.SetBuyingPowerModel(new CustomBuyingPowerModel(this));
}
public void OnData(TradeBars data)
@@ -60,13 +62,13 @@ namespace QuantConnect.Algorithm.CSharp
if (Time.Day > 10 && _security.Holdings.Quantity <= 0)
{
var quantity = CalculateOrderQuantity(_spy, .5m);
Log("MarketOrder: " + quantity);
Log($"MarketOrder: {quantity}");
MarketOrder(_spy, quantity, asynchronous: true); // async needed for partial fill market orders
}
else if (Time.Day > 20 && _security.Holdings.Quantity >= 0)
{
var quantity = CalculateOrderQuantity(_spy, -.5m);
Log("MarketOrder: " + quantity);
Log($"MarketOrder: {quantity}");
MarketOrder(_spy, quantity, asynchronous: true); // async needed for partial fill market orders
}
}
@@ -109,7 +111,7 @@ namespace QuantConnect.Algorithm.CSharp
fill.Status = OrderStatus.PartiallyFilled;
}
_algorithm.Log("CustomFillModel: " + fill);
_algorithm.Log($"CustomFillModel: {fill}");
return fill;
}
@@ -131,7 +133,7 @@ namespace QuantConnect.Algorithm.CSharp
1m,
parameters.Security.Price*parameters.Order.AbsoluteQuantity*0.00001m);
_algorithm.Log("CustomFeeModel: " + fee);
_algorithm.Log($"CustomFeeModel: {fee}");
return new OrderFee(new CashAmount(fee, "USD"));
}
}
@@ -150,11 +152,31 @@ namespace QuantConnect.Algorithm.CSharp
// custom slippage math
var slippage = asset.Price*0.0001m*(decimal) Math.Log10(2*(double) order.AbsoluteQuantity);
_algorithm.Log("CustomSlippageModel: " + slippage);
_algorithm.Log($"CustomSlippageModel: {slippage}");
return slippage;
}
}
public class CustomBuyingPowerModel : BuyingPowerModel
{
private readonly QCAlgorithm _algorithm;
public CustomBuyingPowerModel(QCAlgorithm algorithm)
{
_algorithm = algorithm;
}
public override HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder(
HasSufficientBuyingPowerForOrderParameters parameters)
{
// custom behavior: this model will assume that there is always enough buying power
var hasSufficientBuyingPowerForOrderResult = new HasSufficientBuyingPowerForOrderResult(true);
_algorithm.Log($"CustomBuyingPowerModel: {hasSufficientBuyingPowerForOrderResult.IsSufficient}");
return hasSufficientBuyingPowerForOrderResult;
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>

View File

@@ -0,0 +1,143 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Algorithm simply fetch one-day history prior current time.
/// </summary>
public class DailyHistoryForDailyResolutionRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol[] _symbols = {
QuantConnect.Symbol.Create("GBPUSD", SecurityType.Forex, market: Market.FXCM),
QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, market: Market.Oanda),
QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, market: Market.USA),
QuantConnect.Symbol.Create("BTCUSD", SecurityType.Crypto, market: Market.GDAX),
QuantConnect.Symbol.Create("XAUUSD", SecurityType.Cfd, market: Market.Oanda)
};
private HashSet<Symbol> _received = new HashSet<Symbol>();
public override void Initialize()
{
SetStartDate(2018, 3, 26);
SetEndDate(2018, 4, 10);
foreach (var symbol in _symbols)
{
AddSecurity(symbol, Resolution.Daily);
}
}
public override void OnData(Slice data)
{
using (var enumerator = data.GetEnumerator())
{
while (enumerator.MoveNext())
{
var current = enumerator.Current;
var symbol = current.Key;
_received.Add(symbol);
List<BaseData> history;
if (current.Value.DataType == MarketDataType.QuoteBar)
{
history = History(1, Resolution.Daily).Get<QuoteBar>(symbol).Cast<BaseData>().ToList();
}
else
{
history = History(1, Resolution.Daily).Get<TradeBar>(symbol).Cast<BaseData>().ToList();
}
if (!history.Any()) throw new Exception($"No {symbol} data on the eve of {Time} {Time.DayOfWeek}");
}
}
}
public override void OnEndOfAlgorithm()
{
if (_received.Count != _symbols.Length)
{
throw new Exception($"Data for symbols {string.Join(",", _symbols.Except(_received))} were not received");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "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.084"},
{"Tracking Error", "0.183"},
{"Treynor Ratio", "0"},
{"Total Fees", "$0.00"},
{"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", "371857150"}
};
}
}

View File

@@ -0,0 +1,143 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Algorithm simply fetch one-day history prior current time.
/// </summary>
public class DailyHistoryForMinuteResolutionRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol[] _symbols = {
QuantConnect.Symbol.Create("GBPUSD", SecurityType.Forex, market: Market.FXCM),
QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, market: Market.Oanda),
QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, market: Market.USA),
QuantConnect.Symbol.Create("BTCUSD", SecurityType.Crypto, market: Market.GDAX),
QuantConnect.Symbol.Create("XAUUSD", SecurityType.Cfd, market: Market.Oanda)
};
private HashSet<Symbol> _received = new HashSet<Symbol>();
public override void Initialize()
{
SetStartDate(2018, 3, 26);
SetEndDate(2018, 4, 10);
foreach (var symbol in _symbols)
{
AddSecurity(symbol, Resolution.Minute);
}
Schedule.On(DateRules.EveryDay(), TimeRules.Every(TimeSpan.FromHours(1)), MakeHistoryCall);
}
private void MakeHistoryCall()
{
foreach (var symbol in _symbols)
{
_received.Add(symbol);
bool hasHistory = false;
foreach (var dataType in SubscriptionManager.AvailableDataTypes[symbol.SecurityType])
{
if (dataType == TickType.Quote)
{
hasHistory |= History(1, Resolution.Daily).Get<QuoteBar>(symbol).Any();
}
else
{
hasHistory |= History(1, Resolution.Daily).Get<TradeBar>(symbol).Any();
}
}
if (!hasHistory) throw new Exception($"No {symbol} data on the eve of {Time} {Time.DayOfWeek}");
}
}
public override void OnEndOfAlgorithm()
{
if (_received.Count != _symbols.Length)
{
throw new Exception($"Data for symbols {string.Join(",", _symbols.Except(_received))} were not received");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "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.096"},
{"Tracking Error", "0.212"},
{"Treynor Ratio", "0"},
{"Total Fees", "$0.00"},
{"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", "371857150"}
};
}
}

View File

@@ -26,7 +26,7 @@ namespace QuantConnect.Algorithm.CSharp
/// <summary>
/// Regression test algorithm simply fetch history on boarder of Daylight Saving Time shift
/// </summary>
public class DaylightSavingTimeHistoryRegressionTest : QCAlgorithm, IRegressionAlgorithmDefinition
public class DaylightSavingTimeHistoryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol[] _symbols = new[]
{

View File

@@ -187,12 +187,12 @@ namespace QuantConnect.Algorithm.CSharp
{"Total Trades", "6441"},
{"Average Win", "0.07%"},
{"Average Loss", "-0.07%"},
{"Compounding Annual Return", "13.284%"},
{"Compounding Annual Return", "13.331%"},
{"Drawdown", "10.700%"},
{"Expectancy", "0.061"},
{"Net Profit", "13.284%"},
{"Sharpe Ratio", "0.96"},
{"Probabilistic Sharpe Ratio", "46.111%"},
{"Net Profit", "13.331%"},
{"Sharpe Ratio", "0.963"},
{"Probabilistic Sharpe Ratio", "46.232%"},
{"Loss Rate", "46%"},
{"Win Rate", "54%"},
{"Profit-Loss Ratio", "0.97"},
@@ -200,15 +200,15 @@ namespace QuantConnect.Algorithm.CSharp
{"Beta", "-0.066"},
{"Annual Standard Deviation", "0.121"},
{"Annual Variance", "0.015"},
{"Information Ratio", "0.004"},
{"Information Ratio", "0.006"},
{"Tracking Error", "0.171"},
{"Treynor Ratio", "-1.754"},
{"Total Fees", "$8669.33"},
{"Treynor Ratio", "-1.761"},
{"Total Fees", "$8669.41"},
{"Fitness Score", "0.675"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "1.124"},
{"Return Over Maximum Drawdown", "1.242"},
{"Sortino Ratio", "1.127"},
{"Return Over Maximum Drawdown", "1.246"},
{"Portfolio Turnover", "1.64"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
@@ -223,7 +223,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "-1120327913"}
{"OrderListHash", "-75671425"}
};
}
}

View File

@@ -160,12 +160,12 @@ namespace QuantConnect.Algorithm.CSharp
{"Total Trades", "5059"},
{"Average Win", "0.08%"},
{"Average Loss", "-0.08%"},
{"Compounding Annual Return", "14.901%"},
{"Compounding Annual Return", "14.950%"},
{"Drawdown", "10.600%"},
{"Expectancy", "0.075"},
{"Net Profit", "14.901%"},
{"Sharpe Ratio", "1.068"},
{"Probabilistic Sharpe Ratio", "50.201%"},
{"Net Profit", "14.950%"},
{"Sharpe Ratio", "1.072"},
{"Probabilistic Sharpe Ratio", "50.327%"},
{"Loss Rate", "45%"},
{"Win Rate", "55%"},
{"Profit-Loss Ratio", "0.97"},
@@ -173,15 +173,15 @@ namespace QuantConnect.Algorithm.CSharp
{"Beta", "-0.066"},
{"Annual Standard Deviation", "0.121"},
{"Annual Variance", "0.015"},
{"Information Ratio", "0.08"},
{"Information Ratio", "0.083"},
{"Tracking Error", "0.171"},
{"Treynor Ratio", "-1.963"},
{"Total Fees", "$6806.57"},
{"Treynor Ratio", "-1.971"},
{"Total Fees", "$6806.67"},
{"Fitness Score", "0.694"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "1.261"},
{"Return Over Maximum Drawdown", "1.404"},
{"Sortino Ratio", "1.265"},
{"Return Over Maximum Drawdown", "1.409"},
{"Portfolio Turnover", "1.296"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
@@ -196,7 +196,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "974523768"}
{"OrderListHash", "1142077166"}
};
}
}

View File

@@ -90,13 +90,13 @@ namespace QuantConnect.Algorithm.CSharp
{"Information Ratio", "0"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$14.91"},
{"Total Fees", "$14.92"},
{"Fitness Score", "0.258"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "-27.251"},
{"Portfolio Turnover", "0.515"},
{"Return Over Maximum Drawdown", "-27.228"},
{"Portfolio Turnover", "0.516"},
{"Total Insights Generated", "1"},
{"Total Insights Closed", "1"},
{"Total Insights Analysis Completed", "1"},
@@ -110,7 +110,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "221046152"}
{"OrderListHash", "1296183675"}
};
}
}

View File

@@ -0,0 +1,128 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using QuantConnect.Data;
using QuantConnect.Interfaces;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Checks that the Tick BidPrice and AskPrices are adjusted like Value.
/// </summary>
public class EquityTickQuoteAdjustedModeRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol _ibm;
private bool _bought;
private bool _sold;
public override void Initialize()
{
SetStartDate(2013, 10, 7);
SetEndDate(2013, 10, 11);
SetCash(100000);
_ibm = AddEquity("IBM", Resolution.Tick).Symbol;
}
public override void OnData(Slice data)
{
if (!data.Ticks.ContainsKey(_ibm))
{
return;
}
var security = Securities[_ibm];
if (!security.HasData)
{
return;
}
foreach (var tick in data.Ticks[_ibm])
{
if (tick.BidPrice != 0 && !_bought && ((tick.Value - tick.BidPrice) <= 0.05m))
{
SetHoldings(_ibm, 1);
_bought = true;
return;
}
if (tick.AskPrice != 0 && _bought && !_sold && Math.Abs((double)tick.Value - (double)tick.AskPrice) <= 0.05)
{
Liquidate(_ibm);
_sold = true;
return;
}
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "2"},
{"Average Win", "0%"},
{"Average Loss", "-0.01%"},
{"Compounding Annual Return", "-0.500%"},
{"Drawdown", "0.000%"},
{"Expectancy", "-1"},
{"Net Profit", "-0.006%"},
{"Sharpe Ratio", "0"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "100%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0"},
{"Beta", "0"},
{"Annual Standard Deviation", "0"},
{"Annual Variance", "0"},
{"Information Ratio", "-8.769"},
{"Tracking Error", "0.22"},
{"Treynor Ratio", "0"},
{"Total Fees", "$6.41"},
{"Fitness Score", "0.248"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "-82.815"},
{"Portfolio Turnover", "0.497"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
{"Long Insight Count", "0"},
{"Short Insight Count", "0"},
{"Long/Short Ratio", "100%"},
{"Estimated Monthly Alpha Value", "$0"},
{"Total Accumulated Estimated Alpha Value", "$0"},
{"Mean Population Estimated Insight Value", "$0"},
{"Mean Population Direction", "0%"},
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "1213851303"}
};
}
}

View File

@@ -0,0 +1,172 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using System.Collections.Generic;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Regression algorithm testing doing some history requests outside market hours, reproducing GH issue #4783
/// </summary>
public class ExtendedMarketHoursHistoryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private int _minuteHistoryCount;
private int _hourHistoryCount;
private int _dailyHistoryCount;
/// <summary>
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
/// </summary>
public override void Initialize()
{
SetStartDate(2013, 10, 07);
SetEndDate(2013, 10, 09);
SetCash(100000);
AddEquity("SPY", Resolution.Minute, extendedMarketHours:true, fillDataForward:false);
Schedule.On("RunHistoryCall", DateRules.EveryDay(), TimeRules.Every(TimeSpan.FromHours(1)), RunHistoryCall);
}
private void RunHistoryCall()
{
var spy = Securities["SPY"];
var regularHours = spy.Exchange.Hours.IsOpen(Time, false);
var extendedHours = !regularHours && spy.Exchange.Hours.IsOpen(Time, true);
if (regularHours)
{
_minuteHistoryCount++;
var history = History(spy.Symbol, 5, Resolution.Minute).Count();
if (history != 5)
{
throw new Exception($"Unexpected Minute data count: {history}");
}
}
else
{
if (extendedHours)
{
_hourHistoryCount++;
var history = History(spy.Symbol, 5, Resolution.Hour).Count();
if (history != 5)
{
throw new Exception($"Unexpected Hour data count {history}");
}
}
else
{
_dailyHistoryCount++;
var history = History(spy.Symbol, 5, Resolution.Daily).Count();
if (history != 5)
{
throw new Exception($"Unexpected Daily data count {history}");
}
}
}
}
/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// </summary>
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
public override void OnData(Slice data)
{
if (!Portfolio.Invested)
{
SetHoldings("SPY", 1);
}
}
public override void OnEndOfAlgorithm()
{
if (_minuteHistoryCount != 3 * 6)
{
throw new Exception($"Unexpected minute history requests count {_minuteHistoryCount}");
}
// 6 pre market from 4am to 9am + 4 post market 4pm to 7pm
if (_hourHistoryCount != 3 * 10)
{
throw new Exception($"Unexpected hour history requests count {_hourHistoryCount}");
}
// 0am to 3am + 8pm to 11pm, last day ends at 8pm
if (_dailyHistoryCount != (2 * 8 + 5))
{
throw new Exception($"Unexpected Daily history requests count: {_dailyHistoryCount}");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "20"},
{"Average Win", "0%"},
{"Average Loss", "0.00%"},
{"Compounding Annual Return", "-74.182%"},
{"Drawdown", "2.200%"},
{"Expectancy", "-1"},
{"Net Profit", "-1.046%"},
{"Sharpe Ratio", "-8.269"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "100%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "-0.19"},
{"Beta", "0.579"},
{"Annual Standard Deviation", "0.065"},
{"Annual Variance", "0.004"},
{"Information Ratio", "1.326"},
{"Tracking Error", "0.049"},
{"Treynor Ratio", "-0.934"},
{"Total Fees", "$22.26"},
{"Fitness Score", "0.002"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "-11.855"},
{"Return Over Maximum Drawdown", "-70.945"},
{"Portfolio Turnover", "0.342"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
{"Long Insight Count", "0"},
{"Short Insight Count", "0"},
{"Long/Short Ratio", "100%"},
{"Estimated Monthly Alpha Value", "$0"},
{"Total Accumulated Estimated Alpha Value", "$0"},
{"Mean Population Estimated Insight Value", "$0"},
{"Mean Population Direction", "0%"},
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "-1961710414"}
};
}
}

View File

@@ -141,7 +141,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "699698796"}
{"OrderListHash", "1717552327"}
};
}
}

View File

@@ -179,7 +179,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "-15.574"},
{"Portfolio Turnover", "2.056"},
{"Portfolio Turnover", "2.057"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
@@ -193,7 +193,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "-1311542155"}
{"OrderListHash", "-1116140375"}
};
}
}

View File

@@ -30,6 +30,7 @@ namespace QuantConnect.Algorithm.CSharp
private readonly Dictionary<Symbol, int> _dataPointsPerSymbol = new Dictionary<Symbol, int>();
private bool _added;
private Symbol _eurusd;
private DateTime lastDataTime = DateTime.MinValue;
/// <summary>
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
@@ -51,6 +52,13 @@ namespace QuantConnect.Algorithm.CSharp
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
public override void OnData(Slice data)
{
if (lastDataTime == data.Time)
{
throw new Exception("Duplicate time for current data and last data slice");
}
lastDataTime = data.Time;
if (_added)
{
var eurUsdSubscription = SubscriptionManager.SubscriptionDataConfigService
@@ -94,7 +102,7 @@ namespace QuantConnect.Algorithm.CSharp
var expectedDataPointsPerSymbol = new Dictionary<string, int>
{
{ "EURGBP", 3 },
{ "EURUSD", 29 }
{ "EURUSD", 28 }
};
foreach (var kvp in _dataPointsPerSymbol)

View File

@@ -30,6 +30,7 @@ namespace QuantConnect.Algorithm.CSharp
private readonly Dictionary<Symbol, int> _dataPointsPerSymbol = new Dictionary<Symbol, int>();
private bool _added;
private Symbol _eurusd;
private DateTime lastDataTime = DateTime.MinValue;
/// <summary>
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
@@ -51,6 +52,13 @@ namespace QuantConnect.Algorithm.CSharp
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
public override void OnData(Slice data)
{
if (lastDataTime == data.Time)
{
throw new Exception("Duplicate time for current data and last data slice");
}
lastDataTime = data.Time;
if (_added)
{
var eurUsdSubscription = SubscriptionManager.SubscriptionDataConfigService
@@ -96,7 +104,7 @@ namespace QuantConnect.Algorithm.CSharp
// normal feed
{ "EURGBP", 3 },
// internal feed on the first day, normal feed on the other two days
{ "EURUSD", 3 },
{ "EURUSD", 2 },
// internal feed only
{ "GBPUSD", 0 }
};

View File

@@ -101,7 +101,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Total Trades", "6"},
{"Average Win", "6.02%"},
{"Average Loss", "-2.40%"},
{"Compounding Annual Return", "915.481%"},
{"Compounding Annual Return", "915.480%"},
{"Drawdown", "5.500%"},
{"Expectancy", "1.338"},
{"Net Profit", "11.400%"},
@@ -117,7 +117,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Information Ratio", "9.507"},
{"Tracking Error", "0.507"},
{"Treynor Ratio", "0"},
{"Total Fees", "$2651.00"},
{"Total Fees", "$2651.01"},
{"Fitness Score", "0.467"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
@@ -137,7 +137,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "-1241317053"}
{"OrderListHash", "-89452746"}
};
}
}

View File

@@ -0,0 +1,169 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This regression algorithm tests In The Money (ITM) future option calls across different strike prices.
/// We expect 6 orders from the algorithm, which are:
///
/// * (1) Initial entry, buy ES Call Option (ES19M20 expiring ITM)
/// * (2) Initial entry, sell ES Call Option at different strike (ES20H20 expiring ITM)
/// * [2] Option assignment, opens a position in the underlying (ES20H20, Qty: -1)
/// * [2] Future contract liquidation, due to impending expiry
/// * [1] Option exercise, receive 1 ES19M20 future contract
/// * [1] Liquidate ES19M20 contract, due to expiry
///
/// Additionally, we test delistings for future options and assert that our
/// portfolio holdings reflect the orders the algorithm has submitted.
/// </summary>
public class FutureOptionBuySellCallIntradayRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
public override void Initialize()
{
SetStartDate(2020, 1, 5);
SetEndDate(2020, 6, 30);
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
// which causes delisting events to never be processed, thus leading to options that might never
// be exercised until the next data point arrives.
AddEquity("AAPL", Resolution.Daily);
var es20h20 = AddFutureContract(
QuantConnect.Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
new DateTime(2020, 3, 20)),
Resolution.Minute).Symbol;
var es20m20 = AddFutureContract(
QuantConnect.Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
new DateTime(2020, 6, 19)),
Resolution.Minute).Symbol;
// Select a future option expiring ITM, and adds it to the algorithm.
var esOptions = OptionChainProvider.GetOptionContractList(es20m20, Time)
.Concat(OptionChainProvider.GetOptionContractList(es20h20, Time))
.Where(x => x.ID.StrikePrice == 3200m && x.ID.OptionRight == OptionRight.Call)
.Select(x => AddFutureOptionContract(x, Resolution.Minute).Symbol)
.ToList();
var expectedContracts = new[]
{
QuantConnect.Symbol.CreateOption(es20h20, Market.CME, OptionStyle.American, OptionRight.Call, 3200m,
new DateTime(2020, 3, 20)),
QuantConnect.Symbol.CreateOption(es20m20, Market.CME, OptionStyle.American, OptionRight.Call, 3200m,
new DateTime(2020, 6, 19))
};
foreach (var esOption in esOptions)
{
if (!expectedContracts.Contains(esOption))
{
throw new Exception($"Contract {esOption} was not found in the chain");
}
}
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(es20m20, 1), () =>
{
MarketOrder(esOptions[0], 1);
MarketOrder(esOptions[1], -1);
});
Schedule.On(DateRules.Tomorrow, TimeRules.Noon, () =>
{
Liquidate();
});
}
/// <summary>
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
/// </summary>
/// <exception cref="Exception">The algorithm has holdings</exception>
public override void OnEndOfAlgorithm()
{
if (Portfolio.Invested)
{
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
}
}
/// <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", "6"},
{"Average Win", "2.94%"},
{"Average Loss", "-4.15%"},
{"Compounding Annual Return", "-5.601%"},
{"Drawdown", "5.600%"},
{"Expectancy", "-0.146"},
{"Net Profit", "-2.771%"},
{"Sharpe Ratio", "-0.49"},
{"Probabilistic Sharpe Ratio", "10.583%"},
{"Loss Rate", "50%"},
{"Win Rate", "50%"},
{"Profit-Loss Ratio", "0.71"},
{"Alpha", "-0.043"},
{"Beta", "-0.001"},
{"Annual Standard Deviation", "0.087"},
{"Annual Variance", "0.008"},
{"Information Ratio", "0.96"},
{"Tracking Error", "0.192"},
{"Treynor Ratio", "58.394"},
{"Total Fees", "$14.80"},
{"Fitness Score", "0.018"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "-0.096"},
{"Return Over Maximum Drawdown", "-0.993"},
{"Portfolio Turnover", "0.043"},
{"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", "-290004562"}
};
}
}

View File

@@ -0,0 +1,259 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This regression algorithm tests In The Money (ITM) future option expiry for calls.
/// We expect 3 orders from the algorithm, which are:
///
/// * Initial entry, buy ES Call Option (expiring ITM)
/// * Option exercise, receiving ES future contracts
/// * Future contract liquidation, due to impending expiry
///
/// Additionally, we test delistings for future options and assert that our
/// portfolio holdings reflect the orders the algorithm has submitted.
/// </summary>
public class FutureOptionCallITMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol _es19m20;
private Symbol _esOption;
private Symbol _expectedOptionContract;
public override void Initialize()
{
SetStartDate(2020, 1, 5);
SetEndDate(2020, 6, 30);
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
// which causes delisting events to never be processed, thus leading to options that might never
// be exercised until the next data point arrives.
AddEquity("AAPL", Resolution.Daily);
_es19m20 = AddFutureContract(
QuantConnect.Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
new DateTime(2020, 6, 19)),
Resolution.Minute).Symbol;
// Select a future option expiring ITM, and adds it to the algorithm.
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.Where(x => x.ID.StrikePrice <= 3200m && x.ID.OptionRight == OptionRight.Call)
.OrderByDescending(x => x.ID.StrikePrice)
.Take(1)
.Single(), Resolution.Minute).Symbol;
_expectedOptionContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3200m, new DateTime(2020, 6, 19));
if (_esOption != _expectedOptionContract)
{
throw new Exception($"Contract {_expectedOptionContract} was not found in the chain");
}
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
{
MarketOrder(_esOption, 1);
});
}
public override void OnData(Slice data)
{
// Assert delistings, so that we can make sure that we receive the delisting warnings at
// the expected time. These assertions detect bug #4872
foreach (var delisting in data.Delistings.Values)
{
if (delisting.Type == DelistingType.Warning)
{
if (delisting.Time != new DateTime(2020, 6, 19))
{
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
}
}
if (delisting.Type == DelistingType.Delisted)
{
if (delisting.Time != new DateTime(2020, 6, 20))
{
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
}
}
}
}
public override void OnOrderEvent(OrderEvent orderEvent)
{
if (orderEvent.Status != OrderStatus.Filled)
{
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
return;
}
if (!Securities.ContainsKey(orderEvent.Symbol))
{
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
}
var security = Securities[orderEvent.Symbol];
if (security.Symbol == _es19m20)
{
AssertFutureOptionOrderExercise(orderEvent, security, Securities[_expectedOptionContract]);
}
else if (security.Symbol == _expectedOptionContract)
{
AssertFutureOptionContractOrder(orderEvent, security);
}
else
{
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
}
Log($"{Time:yyyy-MM-dd HH:mm:ss} -- {orderEvent.Symbol} :: Price: {Securities[orderEvent.Symbol].Holdings.Price} Qty: {Securities[orderEvent.Symbol].Holdings.Quantity} Direction: {orderEvent.Direction} Msg: {orderEvent.Message}");
}
private void AssertFutureOptionOrderExercise(OrderEvent orderEvent, Security future, Security optionContract)
{
// We expect the liquidation to occur on the day of the delisting (while the market is open),
// but currently we liquidate at the next market open (AAPL open) which happens to be
// at 9:30:00 Eastern Time. For unknown reasons, the delisting happens two minutes after the
// market open.
// Read more about the issue affecting this test here: https://github.com/QuantConnect/Lean/issues/4980
var expectedLiquidationTimeUtc = new DateTime(2020, 6, 22, 13, 32, 0);
if (orderEvent.Direction == OrderDirection.Sell && future.Holdings.Quantity != 0)
{
// We expect the contract to have been liquidated immediately
throw new Exception($"Did not liquidate existing holdings for Symbol {future.Symbol}");
}
if (orderEvent.Direction == OrderDirection.Sell && orderEvent.UtcTime != expectedLiquidationTimeUtc)
{
throw new Exception($"Liquidated future contract, but not at the expected time. Expected: {expectedLiquidationTimeUtc:yyyy-MM-dd HH:mm:ss} - found {orderEvent.UtcTime:yyyy-MM-dd HH:mm:ss}");
}
// No way to detect option exercise orders or any other kind of special orders
// other than matching strings, for now.
if (orderEvent.Message.Contains("Option Exercise"))
{
if (orderEvent.FillPrice != 3200m)
{
throw new Exception("Option did not exercise at expected strike price (3200)");
}
if (future.Holdings.Quantity != 1)
{
// Here, we expect to have some holdings in the underlying, but not in the future option anymore.
throw new Exception($"Exercised option contract, but we have no holdings for Future {future.Symbol}");
}
if (optionContract.Holdings.Quantity != 0)
{
throw new Exception($"Exercised option contract, but we have holdings for Option contract {optionContract.Symbol}");
}
}
}
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security option)
{
if (orderEvent.Direction == OrderDirection.Buy && option.Holdings.Quantity != 1)
{
throw new Exception($"No holdings were created for option contract {option.Symbol}");
}
if (orderEvent.Direction == OrderDirection.Sell && option.Holdings.Quantity != 0)
{
throw new Exception($"Holdings were found after a filled option exercise");
}
if (orderEvent.Message.Contains("Exercise") && option.Holdings.Quantity != 0)
{
throw new Exception($"Holdings were found after exercising option contract {option.Symbol}");
}
}
/// <summary>
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
/// </summary>
/// <exception cref="Exception">The algorithm has holdings</exception>
public override void OnEndOfAlgorithm()
{
if (Portfolio.Invested)
{
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "3"},
{"Average Win", "1.25%"},
{"Average Loss", "-7.42%"},
{"Compounding Annual Return", "-12.413%"},
{"Drawdown", "6.300%"},
{"Expectancy", "-0.416"},
{"Net Profit", "-6.257%"},
{"Sharpe Ratio", "-1.325"},
{"Probabilistic Sharpe Ratio", "0.004%"},
{"Loss Rate", "50%"},
{"Win Rate", "50%"},
{"Profit-Loss Ratio", "0.17"},
{"Alpha", "-0.102"},
{"Beta", "-0.003"},
{"Annual Standard Deviation", "0.076"},
{"Annual Variance", "0.006"},
{"Information Ratio", "0.673"},
{"Tracking Error", "0.188"},
{"Treynor Ratio", "33.559"},
{"Total Fees", "$7.40"},
{"Fitness Score", "0.008"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "-0.205"},
{"Return Over Maximum Drawdown", "-1.983"},
{"Portfolio Turnover", "0.023"},
{"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", "23301049"}
};
}
}

View File

@@ -0,0 +1,211 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Securities;
using QuantConnect.Securities.Option;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This regression algorithm tests In The Money (ITM) future option expiry for calls.
/// We test to make sure that FOPs have greeks enabled, same as equity options.
/// </summary>
public class FutureOptionCallITMGreeksExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private bool _invested;
private int _onDataCalls;
private Symbol _es19m20;
private Option _esOption;
private Symbol _expectedOptionContract;
public override void Initialize()
{
SetStartDate(2020, 1, 5);
SetEndDate(2020, 6, 30);
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
// which causes delisting events to never be processed, thus leading to options that might never
// be exercised until the next data point arrives.
AddEquity("AAPL", Resolution.Daily);
_es19m20 = AddFutureContract(
QuantConnect.Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
new DateTime(2020, 6, 19)),
Resolution.Minute).Symbol;
// Select a future option expiring ITM, and adds it to the algorithm.
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, new DateTime(2020, 1, 5))
.Where(x => x.ID.StrikePrice <= 3200m && x.ID.OptionRight == OptionRight.Call)
.OrderByDescending(x => x.ID.StrikePrice)
.Take(1)
.Single(), Resolution.Minute);
_esOption.PriceModel = OptionPriceModels.BjerksundStensland();
_expectedOptionContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3200m, new DateTime(2020, 6, 19));
if (_esOption.Symbol != _expectedOptionContract)
{
throw new Exception($"Contract {_expectedOptionContract} was not found in the chain");
}
}
public override void OnData(Slice data)
{
// Let the algo warmup, but without using SetWarmup. Otherwise, we get
// no contracts in the option chain
if (_invested || _onDataCalls++ < 40)
{
return;
}
if (data.OptionChains.Count == 0)
{
return;
}
if (data.OptionChains.Values.All(o => o.Contracts.Values.Any(c => !data.ContainsKey(c.Symbol))))
{
return;
}
if (data.OptionChains.Values.First().Contracts.Count == 0)
{
throw new Exception($"No contracts found in the option {data.OptionChains.Keys.First()}");
}
var deltas = data.OptionChains.Values.OrderByDescending(y => y.Contracts.Values.Sum(x => x.Volume)).First().Contracts.Values.Select(x => x.Greeks.Delta).ToList();
var gammas = data.OptionChains.Values.OrderByDescending(y => y.Contracts.Values.Sum(x => x.Volume)).First().Contracts.Values.Select(x => x.Greeks.Gamma).ToList();
var lambda = data.OptionChains.Values.OrderByDescending(y => y.Contracts.Values.Sum(x => x.Volume)).First().Contracts.Values.Select(x => x.Greeks.Lambda).ToList();
var rho = data.OptionChains.Values.OrderByDescending(y => y.Contracts.Values.Sum(x => x.Volume)).First().Contracts.Values.Select(x => x.Greeks.Rho).ToList();
var theta = data.OptionChains.Values.OrderByDescending(y => y.Contracts.Values.Sum(x => x.Volume)).First().Contracts.Values.Select(x => x.Greeks.Theta).ToList();
var vega = data.OptionChains.Values.OrderByDescending(y => y.Contracts.Values.Sum(x => x.Volume)).First().Contracts.Values.Select(x => x.Greeks.Vega).ToList();
// The commented out test cases all return zero.
// This is because of failure to evaluate the greeks in the option pricing model.
// For now, let's skip those.
if (deltas.Any(d => d == 0))
{
throw new AggregateException("Option contract Delta was equal to zero");
}
if (gammas.Any(g => g == 0))
{
throw new AggregateException("Option contract Gamma was equal to zero");
}
//if (lambda.Any(l => l == 0))
//{
// throw new AggregateException("Option contract Lambda was equal to zero");
//}
if (rho.Any(r => r == 0))
{
throw new AggregateException("Option contract Rho was equal to zero");
}
//if (theta.Any(t => t == 0))
//{
// throw new AggregateException("Option contract Theta was equal to zero");
//}
//if (vega.Any(v => v == 0))
//{
// throw new AggregateException("Option contract Vega was equal to zero");
//}
if (!_invested)
{
SetHoldings(data.OptionChains.Values.First().Contracts.Values.First().Symbol, 1);
_invested = true;
}
}
/// <summary>
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
/// </summary>
/// <exception cref="Exception">The algorithm has holdings</exception>
public override void OnEndOfAlgorithm()
{
if (Portfolio.Invested)
{
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
}
if (!_invested)
{
throw new Exception($"Never checked greeks, maybe we have no option data?");
}
}
/// <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", "3"},
{"Average Win", "28.04%"},
{"Average Loss", "-62.81%"},
{"Compounding Annual Return", "-78.165%"},
{"Drawdown", "52.400%"},
{"Expectancy", "-0.277"},
{"Net Profit", "-52.379%"},
{"Sharpe Ratio", "-0.865"},
{"Probabilistic Sharpe Ratio", "0.019%"},
{"Loss Rate", "50%"},
{"Win Rate", "50%"},
{"Profit-Loss Ratio", "0.45"},
{"Alpha", "-0.596"},
{"Beta", "-0.031"},
{"Annual Standard Deviation", "0.681"},
{"Annual Variance", "0.463"},
{"Information Ratio", "-0.514"},
{"Tracking Error", "0.703"},
{"Treynor Ratio", "18.748"},
{"Total Fees", "$66.60"},
{"Fitness Score", "0.157"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "-0.133"},
{"Return Over Maximum Drawdown", "-1.492"},
{"Portfolio Turnover", "0.411"},
{"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", "151392833"}
};
}
}

View File

@@ -0,0 +1,226 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This regression algorithm tests Out of The Money (OTM) future option expiry for calls.
/// We expect 1 order from the algorithm, which are:
///
/// * Initial entry, buy ES Call Option (expiring OTM)
/// - contract expires worthless, not exercised, so never opened a position in the underlying
///
/// Additionally, we test delistings for future options and assert that our
/// portfolio holdings reflect the orders the algorithm has submitted.
/// </summary>
/// <remarks>
/// Total Trades in regression algorithm should be 1, but expiration is counted as a trade.
/// See related issue: https://github.com/QuantConnect/Lean/issues/4854
/// </remarks>
public class FutureOptionCallOTMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol _es19m20;
private Symbol _esOption;
private Symbol _expectedContract;
public override void Initialize()
{
SetStartDate(2020, 1, 5);
SetEndDate(2020, 6, 30);
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
// which causes delisting events to never be processed, thus leading to options that might never
// be exercised until the next data point arrives.
AddEquity("AAPL", Resolution.Daily);
_es19m20 = AddFutureContract(
QuantConnect.Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
new DateTime(2020, 6, 19)),
Resolution.Minute).Symbol;
// Select a future option call expiring OTM, and adds it to the algorithm.
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.Where(x => x.ID.StrikePrice >= 3300m && x.ID.OptionRight == OptionRight.Call)
.OrderBy(x => x.ID.StrikePrice)
.Take(1)
.Single(), Resolution.Minute).Symbol;
_expectedContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3300m, new DateTime(2020, 6, 19));
if (_esOption != _expectedContract)
{
throw new Exception($"Contract {_expectedContract} was not found in the chain");
}
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
{
MarketOrder(_esOption, 1);
});
}
public override void OnData(Slice data)
{
// Assert delistings, so that we can make sure that we receive the delisting warnings at
// the expected time. These assertions detect bug #4872
foreach (var delisting in data.Delistings.Values)
{
if (delisting.Type == DelistingType.Warning)
{
if (delisting.Time != new DateTime(2020, 6, 19))
{
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
}
}
if (delisting.Type == DelistingType.Delisted)
{
if (delisting.Time != new DateTime(2020, 6, 20))
{
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
}
}
}
}
public override void OnOrderEvent(OrderEvent orderEvent)
{
if (orderEvent.Status != OrderStatus.Filled)
{
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
return;
}
if (!Securities.ContainsKey(orderEvent.Symbol))
{
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
}
var security = Securities[orderEvent.Symbol];
if (security.Symbol == _es19m20)
{
throw new Exception("Invalid state: did not expect a position for the underlying to be opened, since this contract expires OTM");
}
if (security.Symbol == _expectedContract)
{
AssertFutureOptionContractOrder(orderEvent, security);
}
else
{
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
}
Log($"{orderEvent}");
}
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security option)
{
if (orderEvent.Direction == OrderDirection.Buy && option.Holdings.Quantity != 1)
{
throw new Exception($"No holdings were created for option contract {option.Symbol}");
}
if (orderEvent.Direction == OrderDirection.Sell && option.Holdings.Quantity != 0)
{
throw new Exception("Holdings were found after a filled option exercise");
}
if (orderEvent.Direction == OrderDirection.Sell && !orderEvent.Message.Contains("OTM"))
{
throw new Exception("Contract did not expire OTM");
}
if (orderEvent.Message.Contains("Exercise"))
{
throw new Exception("Exercised option, even though it expires OTM");
}
}
/// <summary>
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
/// </summary>
/// <exception cref="Exception">The algorithm has holdings</exception>
public override void OnEndOfAlgorithm()
{
if (Portfolio.Invested)
{
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "2"},
{"Average Win", "0%"},
{"Average Loss", "-4.03%"},
{"Compounding Annual Return", "-8.088%"},
{"Drawdown", "4.000%"},
{"Expectancy", "-1"},
{"Net Profit", "-4.029%"},
{"Sharpe Ratio", "-1.274"},
{"Probabilistic Sharpe Ratio", "0.015%"},
{"Loss Rate", "100%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "-0.066"},
{"Beta", "-0.002"},
{"Annual Standard Deviation", "0.052"},
{"Annual Variance", "0.003"},
{"Information Ratio", "0.9"},
{"Tracking Error", "0.179"},
{"Treynor Ratio", "28.537"},
{"Total Fees", "$3.70"},
{"Fitness Score", "0"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "-0.183"},
{"Return Over Maximum Drawdown", "-2.007"},
{"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", "-1116221764"}
};
}
}

View File

@@ -0,0 +1,259 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This regression algorithm tests In The Money (ITM) future option expiry for puts.
/// We expect 3 orders from the algorithm, which are:
///
/// * Initial entry, buy ES Put Option (expiring ITM) (buy, qty 1)
/// * Option exercise, receiving short ES future contracts (sell, qty -1)
/// * Future contract liquidation, due to impending expiry (buy qty 1)
///
/// Additionally, we test delistings for future options and assert that our
/// portfolio holdings reflect the orders the algorithm has submitted.
/// </summary>
public class FutureOptionPutITMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol _es19m20;
private Symbol _esOption;
private Symbol _expectedContract;
public override void Initialize()
{
SetStartDate(2020, 1, 5);
SetEndDate(2020, 6, 30);
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
// which causes delisting events to never be processed, thus leading to options that might never
// be exercised until the next data point arrives.
AddEquity("AAPL", Resolution.Daily);
_es19m20 = AddFutureContract(
QuantConnect.Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
new DateTime(2020, 6, 19)),
Resolution.Minute).Symbol;
// Select a future option expiring ITM, and adds it to the algorithm.
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.Where(x => x.ID.StrikePrice >= 3300m && x.ID.OptionRight == OptionRight.Put)
.OrderBy(x => x.ID.StrikePrice)
.Take(1)
.Single(), Resolution.Minute).Symbol;
_expectedContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3300m, new DateTime(2020, 6, 19));
if (_esOption != _expectedContract)
{
throw new Exception($"Contract {_expectedContract} was not found in the chain");
}
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
{
MarketOrder(_esOption, 1);
});
}
public override void OnData(Slice data)
{
// Assert delistings, so that we can make sure that we receive the delisting warnings at
// the expected time. These assertions detect bug #4872
foreach (var delisting in data.Delistings.Values)
{
if (delisting.Type == DelistingType.Warning)
{
if (delisting.Time != new DateTime(2020, 6, 19))
{
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
}
}
if (delisting.Type == DelistingType.Delisted)
{
if (delisting.Time != new DateTime(2020, 6, 20))
{
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
}
}
}
}
public override void OnOrderEvent(OrderEvent orderEvent)
{
if (orderEvent.Status != OrderStatus.Filled)
{
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
return;
}
if (!Securities.ContainsKey(orderEvent.Symbol))
{
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
}
var security = Securities[orderEvent.Symbol];
if (security.Symbol == _es19m20)
{
AssertFutureOptionOrderExercise(orderEvent, security, Securities[_expectedContract]);
}
else if (security.Symbol == _expectedContract)
{
AssertFutureOptionContractOrder(orderEvent, security);
}
else
{
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
}
Log($"{Time:yyyy-MM-dd HH:mm:ss} -- {orderEvent.Symbol} :: Price: {Securities[orderEvent.Symbol].Holdings.Price} Qty: {Securities[orderEvent.Symbol].Holdings.Quantity} Direction: {orderEvent.Direction} Msg: {orderEvent.Message}");
}
private void AssertFutureOptionOrderExercise(OrderEvent orderEvent, Security future, Security optionContract)
{
// We expect the liquidation to occur on the day of the delisting (while the market is open),
// but currently we liquidate at the next market open (AAPL open) which happens to be
// at 9:30:00 Eastern Time. For unknown reasons, the delisting happens two minutes after the
// market open.
// Read more about the issue affecting this test here: https://github.com/QuantConnect/Lean/issues/4980
var expectedLiquidationTimeUtc = new DateTime(2020, 6, 22, 13, 32, 0);
if (orderEvent.Direction == OrderDirection.Buy && future.Holdings.Quantity != 0)
{
// We expect the contract to have been liquidated immediately
throw new Exception($"Did not liquidate existing holdings for Symbol {future.Symbol}");
}
if (orderEvent.Direction == OrderDirection.Buy && orderEvent.UtcTime != expectedLiquidationTimeUtc)
{
throw new Exception($"Liquidated future contract, but not at the expected time. Expected: {expectedLiquidationTimeUtc:yyyy-MM-dd HH:mm:ss} - found {orderEvent.UtcTime:yyyy-MM-dd HH:mm:ss}");
}
// No way to detect option exercise orders or any other kind of special orders
// other than matching strings, for now.
if (orderEvent.Message.Contains("Option Exercise"))
{
if (orderEvent.FillPrice != 3300m)
{
throw new Exception("Option did not exercise at expected strike price (3300)");
}
if (future.Holdings.Quantity != -1)
{
// Here, we expect to have some holdings in the underlying, but not in the future option anymore.
throw new Exception($"Exercised option contract, but we have no holdings for Future {future.Symbol}");
}
if (optionContract.Holdings.Quantity != 0)
{
throw new Exception($"Exercised option contract, but we have holdings for Option contract {optionContract.Symbol}");
}
}
}
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security option)
{
if (orderEvent.Direction == OrderDirection.Buy && option.Holdings.Quantity != 1)
{
throw new Exception($"No holdings were created for option contract {option.Symbol}");
}
if (orderEvent.Direction == OrderDirection.Sell && option.Holdings.Quantity != 0)
{
throw new Exception($"Holdings were found after a filled option exercise");
}
if (orderEvent.Message.Contains("Exercise") && option.Holdings.Quantity != 0)
{
throw new Exception($"Holdings were found after exercising option contract {option.Symbol}");
}
}
/// <summary>
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
/// </summary>
/// <exception cref="Exception">The algorithm has holdings</exception>
public override void OnEndOfAlgorithm()
{
if (Portfolio.Invested)
{
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "3"},
{"Average Win", "4.18%"},
{"Average Loss", "-8.27%"},
{"Compounding Annual Return", "-8.879%"},
{"Drawdown", "4.400%"},
{"Expectancy", "-0.247"},
{"Net Profit", "-4.432%"},
{"Sharpe Ratio", "-1.391"},
{"Probabilistic Sharpe Ratio", "0.002%"},
{"Loss Rate", "50%"},
{"Win Rate", "50%"},
{"Profit-Loss Ratio", "0.51"},
{"Alpha", "-0.073"},
{"Beta", "-0.002"},
{"Annual Standard Deviation", "0.052"},
{"Annual Variance", "0.003"},
{"Information Ratio", "0.863"},
{"Tracking Error", "0.179"},
{"Treynor Ratio", "38.46"},
{"Total Fees", "$7.40"},
{"Fitness Score", "0.008"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "-0.224"},
{"Return Over Maximum Drawdown", "-2.003"},
{"Portfolio Turnover", "0.023"},
{"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", "-675079082"}
};
}
}

View File

@@ -0,0 +1,225 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This regression algorithm tests Out of The Money (OTM) future option expiry for puts.
/// We expect 1 order from the algorithm, which are:
///
/// * Initial entry, buy ES Put Option (expiring OTM)
/// - contract expires worthless, not exercised, so never opened a position in the underlying
///
/// Additionally, we test delistings for future options and assert that our
/// portfolio holdings reflect the orders the algorithm has submitted.
/// </summary>
/// <remarks>
/// Total Trades in regression algorithm should be 1, but expiration is counted as a trade.
/// </remarks>
public class FutureOptionPutOTMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol _es19m20;
private Symbol _esOption;
private Symbol _expectedContract;
public override void Initialize()
{
SetStartDate(2020, 1, 5);
SetEndDate(2020, 6, 30);
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
// which causes delisting events to never be processed, thus leading to options that might never
// be exercised until the next data point arrives.
AddEquity("AAPL", Resolution.Daily);
_es19m20 = AddFutureContract(
QuantConnect.Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
new DateTime(2020, 6, 19)),
Resolution.Minute).Symbol;
// Select a future option expiring ITM, and adds it to the algorithm.
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.Where(x => x.ID.StrikePrice <= 3150m && x.ID.OptionRight == OptionRight.Put)
.OrderByDescending(x => x.ID.StrikePrice)
.Take(1)
.Single(), Resolution.Minute).Symbol;
_expectedContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3150m, new DateTime(2020, 6, 19));
if (_esOption != _expectedContract)
{
throw new Exception($"Contract {_expectedContract} was not found in the chain");
}
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
{
MarketOrder(_esOption, 1);
});
}
public override void OnData(Slice data)
{
// Assert delistings, so that we can make sure that we receive the delisting warnings at
// the expected time. These assertions detect bug #4872
foreach (var delisting in data.Delistings.Values)
{
if (delisting.Type == DelistingType.Warning)
{
if (delisting.Time != new DateTime(2020, 6, 19))
{
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
}
}
if (delisting.Type == DelistingType.Delisted)
{
if (delisting.Time != new DateTime(2020, 6, 20))
{
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
}
}
}
}
public override void OnOrderEvent(OrderEvent orderEvent)
{
if (orderEvent.Status != OrderStatus.Filled)
{
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
return;
}
if (!Securities.ContainsKey(orderEvent.Symbol))
{
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
}
var security = Securities[orderEvent.Symbol];
if (security.Symbol == _es19m20)
{
throw new Exception("Invalid state: did not expect a position for the underlying to be opened, since this contract expires OTM");
}
if (security.Symbol == _expectedContract)
{
AssertFutureOptionContractOrder(orderEvent, security);
}
else
{
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
}
Log($"{orderEvent}");
}
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security option)
{
if (orderEvent.Direction == OrderDirection.Buy && option.Holdings.Quantity != 1)
{
throw new Exception($"No holdings were created for option contract {option.Symbol}");
}
if (orderEvent.Direction == OrderDirection.Sell && option.Holdings.Quantity != 0)
{
throw new Exception("Holdings were found after a filled option exercise");
}
if (orderEvent.Direction == OrderDirection.Sell && !orderEvent.Message.Contains("OTM"))
{
throw new Exception("Contract did not expire OTM");
}
if (orderEvent.Message.Contains("Exercise"))
{
throw new Exception("Exercised option, even though it expires OTM");
}
}
/// <summary>
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
/// </summary>
/// <exception cref="Exception">The algorithm has holdings</exception>
public override void OnEndOfAlgorithm()
{
if (Portfolio.Invested)
{
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "2"},
{"Average Win", "0%"},
{"Average Loss", "-5.12%"},
{"Compounding Annual Return", "-10.212%"},
{"Drawdown", "5.100%"},
{"Expectancy", "-1"},
{"Net Profit", "-5.116%"},
{"Sharpe Ratio", "-1.26"},
{"Probabilistic Sharpe Ratio", "0.016%"},
{"Loss Rate", "100%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "-0.084"},
{"Beta", "-0.003"},
{"Annual Standard Deviation", "0.066"},
{"Annual Variance", "0.004"},
{"Information Ratio", "0.785"},
{"Tracking Error", "0.184"},
{"Treynor Ratio", "28.158"},
{"Total Fees", "$3.70"},
{"Fitness Score", "0"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "-0.181"},
{"Return Over Maximum Drawdown", "-1.995"},
{"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", "515984318"}
};
}
}

View File

@@ -0,0 +1,238 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This regression algorithm tests In The Money (ITM) future option expiry for short calls.
/// We expect 3 orders from the algorithm, which are:
///
/// * Initial entry, sell ES Call Option (expiring ITM)
/// * Option assignment, sell 1 contract of the underlying (ES)
/// * Future contract expiry, liquidation (buy 1 ES future)
///
/// Additionally, we test delistings for future options and assert that our
/// portfolio holdings reflect the orders the algorithm has submitted.
/// </summary>
public class FutureOptionShortCallITMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol _es19m20;
private Symbol _esOption;
private Symbol _expectedContract;
public override void Initialize()
{
SetStartDate(2020, 1, 5);
SetEndDate(2020, 6, 30);
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
// which causes delisting events to never be processed, thus leading to options that might never
// be exercised until the next data point arrives.
AddEquity("AAPL", Resolution.Daily);
_es19m20 = AddFutureContract(
QuantConnect.Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
new DateTime(2020, 6, 19)),
Resolution.Minute).Symbol;
// Select a future option expiring ITM, and adds it to the algorithm.
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.Where(x => x.ID.StrikePrice <= 3100m && x.ID.OptionRight == OptionRight.Call)
.OrderByDescending(x => x.ID.StrikePrice)
.Take(1)
.Single(), Resolution.Minute).Symbol;
_expectedContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3100m, new DateTime(2020, 6, 19));
if (_esOption != _expectedContract)
{
throw new Exception($"Contract {_expectedContract} was not found in the chain");
}
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
{
MarketOrder(_esOption, -1);
});
}
public override void OnData(Slice data)
{
// Assert delistings, so that we can make sure that we receive the delisting warnings at
// the expected time. These assertions detect bug #4872
foreach (var delisting in data.Delistings.Values)
{
if (delisting.Type == DelistingType.Warning)
{
if (delisting.Time != new DateTime(2020, 6, 19))
{
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
}
}
if (delisting.Type == DelistingType.Delisted)
{
if (delisting.Time != new DateTime(2020, 6, 20))
{
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
}
}
}
}
public override void OnOrderEvent(OrderEvent orderEvent)
{
if (orderEvent.Status != OrderStatus.Filled)
{
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
return;
}
if (!Securities.ContainsKey(orderEvent.Symbol))
{
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
}
var security = Securities[orderEvent.Symbol];
if (security.Symbol == _es19m20)
{
AssertFutureOptionOrderExercise(orderEvent, security, Securities[_expectedContract]);
}
else if (security.Symbol == _expectedContract)
{
AssertFutureOptionContractOrder(orderEvent, security);
}
else
{
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
}
Log($"{orderEvent}");
}
private void AssertFutureOptionOrderExercise(OrderEvent orderEvent, Security future, Security optionContract)
{
if (orderEvent.Message.Contains("Assignment"))
{
if (orderEvent.FillPrice != 3100m)
{
throw new Exception("Option was not assigned at expected strike price (3100)");
}
if (orderEvent.Direction != OrderDirection.Sell || future.Holdings.Quantity != -1)
{
throw new Exception($"Expected Qty: -1 futures holdings for assigned future {future.Symbol}, found {future.Holdings.Quantity}");
}
return;
}
if (orderEvent.Direction == OrderDirection.Buy && future.Holdings.Quantity != 0)
{
// We buy back the underlying at expiration, so we expect a neutral position then
throw new Exception($"Expected no holdings when liquidating future contract {future.Symbol}");
}
}
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security option)
{
if (orderEvent.Direction == OrderDirection.Sell && option.Holdings.Quantity != -1)
{
throw new Exception($"No holdings were created for option contract {option.Symbol}");
}
if (orderEvent.IsAssignment && option.Holdings.Quantity != 0)
{
throw new Exception($"Holdings were found after option contract was assigned: {option.Symbol}");
}
}
/// <summary>
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
/// </summary>
/// <exception cref="Exception">The algorithm has holdings</exception>
public override void OnEndOfAlgorithm()
{
if (Portfolio.Invested)
{
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "3"},
{"Average Win", "10.05%"},
{"Average Loss", "-5.60%"},
{"Compounding Annual Return", "8.121%"},
{"Drawdown", "0.500%"},
{"Expectancy", "0.396"},
{"Net Profit", "3.880%"},
{"Sharpe Ratio", "1.192"},
{"Probabilistic Sharpe Ratio", "58.203%"},
{"Loss Rate", "50%"},
{"Win Rate", "50%"},
{"Profit-Loss Ratio", "1.79"},
{"Alpha", "0.069"},
{"Beta", "0.003"},
{"Annual Standard Deviation", "0.057"},
{"Annual Variance", "0.003"},
{"Information Ratio", "1.641"},
{"Tracking Error", "0.18"},
{"Treynor Ratio", "22.101"},
{"Total Fees", "$7.40"},
{"Fitness Score", "0.021"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "17.255"},
{"Portfolio Turnover", "0.021"},
{"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", "1118389718"}
};
}
}

View File

@@ -0,0 +1,219 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This regression algorithm tests Out of The Money (OTM) future option expiry for short calls.
/// We expect 1 order from the algorithm, which are:
///
/// * Initial entry, sell ES Call Option (expiring OTM)
/// - Profit the option premium, since the option was not assigned.
///
/// Additionally, we test delistings for future options and assert that our
/// portfolio holdings reflect the orders the algorithm has submitted.
/// </summary>
public class FutureOptionShortCallOTMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol _es19m20;
private Symbol _esOption;
private Symbol _expectedContract;
public override void Initialize()
{
SetStartDate(2020, 1, 5);
SetEndDate(2020, 6, 30);
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
// which causes delisting events to never be processed, thus leading to options that might never
// be exercised until the next data point arrives.
AddEquity("AAPL", Resolution.Daily);
_es19m20 = AddFutureContract(
QuantConnect.Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
new DateTime(2020, 6, 19)),
Resolution.Minute).Symbol;
// Select a future option expiring ITM, and adds it to the algorithm.
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.Where(x => x.ID.StrikePrice >= 3400m && x.ID.OptionRight == OptionRight.Call)
.OrderBy(x => x.ID.StrikePrice)
.Take(1)
.Single(), Resolution.Minute).Symbol;
_expectedContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3400m, new DateTime(2020, 6, 19));
if (_esOption != _expectedContract)
{
throw new Exception($"Contract {_expectedContract} was not found in the chain");
}
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
{
MarketOrder(_esOption, -1);
});
}
public override void OnData(Slice data)
{
// Assert delistings, so that we can make sure that we receive the delisting warnings at
// the expected time. These assertions detect bug #4872
foreach (var delisting in data.Delistings.Values)
{
if (delisting.Type == DelistingType.Warning)
{
if (delisting.Time != new DateTime(2020, 6, 19))
{
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
}
}
if (delisting.Type == DelistingType.Delisted)
{
if (delisting.Time != new DateTime(2020, 6, 20))
{
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
}
}
}
}
public override void OnOrderEvent(OrderEvent orderEvent)
{
if (orderEvent.Status != OrderStatus.Filled)
{
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
return;
}
if (!Securities.ContainsKey(orderEvent.Symbol))
{
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
}
var security = Securities[orderEvent.Symbol];
if (security.Symbol == _es19m20)
{
throw new Exception($"Expected no order events for underlying Symbol {security.Symbol}");
}
if (security.Symbol == _expectedContract)
{
AssertFutureOptionContractOrder(orderEvent, security);
}
else
{
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
}
Log($"{orderEvent}");
}
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security optionContract)
{
if (orderEvent.Direction == OrderDirection.Sell && optionContract.Holdings.Quantity != -1)
{
throw new Exception($"No holdings were created for option contract {optionContract.Symbol}");
}
if (orderEvent.Direction == OrderDirection.Buy && optionContract.Holdings.Quantity != 0)
{
throw new Exception("Expected no options holdings after closing position");
}
if (orderEvent.IsAssignment)
{
throw new Exception($"Assignment was not expected for {orderEvent.Symbol}");
}
}
/// <summary>
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
/// </summary>
/// <exception cref="Exception">The algorithm has holdings</exception>
public override void OnEndOfAlgorithm()
{
if (Portfolio.Invested)
{
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "2"},
{"Average Win", "1.81%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "3.745%"},
{"Drawdown", "0.000%"},
{"Expectancy", "0"},
{"Net Profit", "1.809%"},
{"Sharpe Ratio", "1.292"},
{"Probabilistic Sharpe Ratio", "65.890%"},
{"Loss Rate", "0%"},
{"Win Rate", "100%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0.031"},
{"Beta", "0.001"},
{"Annual Standard Deviation", "0.024"},
{"Annual Variance", "0.001"},
{"Information Ratio", "1.496"},
{"Tracking Error", "0.173"},
{"Treynor Ratio", "27.281"},
{"Total Fees", "$3.70"},
{"Fitness Score", "0"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "95.176"},
{"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", "1364902860"}
};
}
}

View File

@@ -0,0 +1,235 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This regression algorithm tests In The Money (ITM) future option expiry for short puts.
/// We expect 3 orders from the algorithm, which are:
///
/// * Initial entry, sell ES Put Option (expiring ITM)
/// * Option assignment, buy 1 contract of the underlying (ES)
/// * Future contract expiry, liquidation (sell 1 ES future)
///
/// Additionally, we test delistings for future options and assert that our
/// portfolio holdings reflect the orders the algorithm has submitted.
/// </summary>
public class FutureOptionShortPutITMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol _es19m20;
private Symbol _esOption;
private Symbol _expectedContract;
public override void Initialize()
{
SetStartDate(2020, 1, 5);
SetEndDate(2020, 6, 30);
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
// which causes delisting events to never be processed, thus leading to options that might never
// be exercised until the next data point arrives.
AddEquity("AAPL", Resolution.Daily);
_es19m20 = AddFutureContract(
QuantConnect.Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
new DateTime(2020, 6, 19)),
Resolution.Minute).Symbol;
// Select a future option expiring ITM, and adds it to the algorithm.
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.Where(x => x.ID.StrikePrice <= 3400m && x.ID.OptionRight == OptionRight.Put)
.OrderByDescending(x => x.ID.StrikePrice)
.Take(1)
.Single(), Resolution.Minute).Symbol;
_expectedContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3400m, new DateTime(2020, 6, 19));
if (_esOption != _expectedContract)
{
throw new Exception($"Contract {_expectedContract} was not found in the chain");
}
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
{
MarketOrder(_esOption, -1);
});
}
public override void OnData(Slice data)
{
// Assert delistings, so that we can make sure that we receive the delisting warnings at
// the expected time. These assertions detect bug #4872
foreach (var delisting in data.Delistings.Values)
{
if (delisting.Type == DelistingType.Warning)
{
if (delisting.Time != new DateTime(2020, 6, 19))
{
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
}
}
if (delisting.Type == DelistingType.Delisted)
{
if (delisting.Time != new DateTime(2020, 6, 20))
{
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
}
}
}
}
public override void OnOrderEvent(OrderEvent orderEvent)
{
if (orderEvent.Status != OrderStatus.Filled)
{
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
return;
}
if (!Securities.ContainsKey(orderEvent.Symbol))
{
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
}
var security = Securities[orderEvent.Symbol];
if (security.Symbol == _es19m20)
{
AssertFutureOptionOrderExercise(orderEvent, security, Securities[_expectedContract]);
}
else if (security.Symbol == _expectedContract)
{
AssertFutureOptionContractOrder(orderEvent, security);
}
else
{
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
}
Log($"{orderEvent}");
}
private void AssertFutureOptionOrderExercise(OrderEvent orderEvent, Security future, Security optionContract)
{
if (orderEvent.Message.Contains("Assignment"))
{
if (orderEvent.FillPrice != 3400)
{
throw new Exception("Option was not assigned at expected strike price (3400)");
}
if (orderEvent.Direction != OrderDirection.Buy || future.Holdings.Quantity != 1)
{
throw new Exception($"Expected Qty: 1 futures holdings for assigned future {future.Symbol}, found {future.Holdings.Quantity}");
}
}
if (!orderEvent.Message.Contains("Assignment") && orderEvent.Direction == OrderDirection.Sell && future.Holdings.Quantity != 0)
{
// We buy back the underlying at expiration, so we expect a neutral position then
throw new Exception($"Expected no holdings when liquidating future contract {future.Symbol}");
}
}
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security option)
{
if (orderEvent.Direction == OrderDirection.Sell && option.Holdings.Quantity != -1)
{
throw new Exception($"No holdings were created for option contract {option.Symbol}");
}
if (orderEvent.IsAssignment && option.Holdings.Quantity != 0)
{
throw new Exception($"Holdings were found after option contract was assigned: {option.Symbol}");
}
}
/// <summary>
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
/// </summary>
/// <exception cref="Exception">The algorithm has holdings</exception>
public override void OnEndOfAlgorithm()
{
if (Portfolio.Invested)
{
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "3"},
{"Average Win", "10.18%"},
{"Average Loss", "-8.02%"},
{"Compounding Annual Return", "2.773%"},
{"Drawdown", "0.500%"},
{"Expectancy", "0.135"},
{"Net Profit", "1.343%"},
{"Sharpe Ratio", "0.939"},
{"Probabilistic Sharpe Ratio", "46.842%"},
{"Loss Rate", "50%"},
{"Win Rate", "50%"},
{"Profit-Loss Ratio", "1.27"},
{"Alpha", "0.023"},
{"Beta", "0.002"},
{"Annual Standard Deviation", "0.025"},
{"Annual Variance", "0.001"},
{"Information Ratio", "1.45"},
{"Tracking Error", "0.173"},
{"Treynor Ratio", "14.62"},
{"Total Fees", "$7.40"},
{"Fitness Score", "0.021"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "5.815"},
{"Portfolio Turnover", "0.022"},
{"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", "980293281"}
};
}
}

View File

@@ -0,0 +1,218 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This regression algorithm tests Out of The Money (OTM) future option expiry for short puts.
/// We expect 1 order from the algorithm, which are:
///
/// * Initial entry, sell ES Put Option (expiring OTM)
/// - Profit the option premium, since the option was not assigned.
///
/// Additionally, we test delistings for future options and assert that our
/// portfolio holdings reflect the orders the algorithm has submitted.
/// </summary>
public class FutureOptionShortPutOTMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol _es19m20;
private Symbol _esOption;
private Symbol _expectedContract;
public override void Initialize()
{
SetStartDate(2020, 1, 5);
SetEndDate(2020, 6, 30);
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
// which causes delisting events to never be processed, thus leading to options that might never
// be exercised until the next data point arrives.
AddEquity("AAPL", Resolution.Daily);
_es19m20 = AddFutureContract(
QuantConnect.Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
new DateTime(2020, 6, 19)),
Resolution.Minute).Symbol;
// Select a future option expiring ITM, and adds it to the algorithm.
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.Where(x => x.ID.StrikePrice <= 3000m && x.ID.OptionRight == OptionRight.Put)
.OrderByDescending(x => x.ID.StrikePrice)
.Take(1)
.Single(), Resolution.Minute).Symbol;
_expectedContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3000m, new DateTime(2020, 6, 19));
if (_esOption != _expectedContract)
{
throw new Exception($"Contract {_expectedContract} was not found in the chain");
}
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
{
MarketOrder(_esOption, -1);
});
}
public override void OnData(Slice data)
{
// Assert delistings, so that we can make sure that we receive the delisting warnings at
// the expected time. These assertions detect bug #4872
foreach (var delisting in data.Delistings.Values)
{
if (delisting.Type == DelistingType.Warning)
{
if (delisting.Time != new DateTime(2020, 6, 19))
{
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
}
}
if (delisting.Type == DelistingType.Delisted)
{
if (delisting.Time != new DateTime(2020, 6, 20))
{
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
}
}
}
}
public override void OnOrderEvent(OrderEvent orderEvent)
{
if (orderEvent.Status != OrderStatus.Filled)
{
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
return;
}
if (!Securities.ContainsKey(orderEvent.Symbol))
{
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
}
var security = Securities[orderEvent.Symbol];
if (security.Symbol == _es19m20)
{
throw new Exception($"Expected no order events for underlying Symbol {security.Symbol}");
}
if (security.Symbol == _expectedContract)
{
AssertFutureOptionContractOrder(orderEvent, security);
}
else
{
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
}
Log($"{orderEvent}");
}
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security option)
{
if (orderEvent.Direction == OrderDirection.Sell && option.Holdings.Quantity != -1)
{
throw new Exception($"No holdings were created for option contract {option.Symbol}");
}
if (orderEvent.Direction == OrderDirection.Buy && option.Holdings.Quantity != 0)
{
throw new Exception("Expected no options holdings after closing position");
}
if (orderEvent.IsAssignment)
{
throw new Exception($"Assignment was not expected for {orderEvent.Symbol}");
}
}
/// <summary>
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
/// </summary>
/// <exception cref="Exception">The algorithm has holdings</exception>
public override void OnEndOfAlgorithm()
{
if (Portfolio.Invested)
{
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp, Language.Python };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "2"},
{"Average Win", "3.28%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "6.852%"},
{"Drawdown", "0.000%"},
{"Expectancy", "0"},
{"Net Profit", "3.284%"},
{"Sharpe Ratio", "1.319"},
{"Probabilistic Sharpe Ratio", "66.574%"},
{"Loss Rate", "0%"},
{"Win Rate", "100%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0.058"},
{"Beta", "0.002"},
{"Annual Standard Deviation", "0.043"},
{"Annual Variance", "0.002"},
{"Information Ratio", "1.614"},
{"Tracking Error", "0.176"},
{"Treynor Ratio", "28.2"},
{"Total Fees", "$3.70"},
{"Fitness Score", "0"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "150.252"},
{"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", "-418839052"}
};
}
}

View File

@@ -53,7 +53,7 @@ namespace QuantConnect.Algorithm.CSharp
}
var firstBar = history.First().Bars.GetValue(symbol);
if (firstBar.EndTime != new DateTime(1998, 3, 3) || firstBar.Close != 26.3607004m)
if (firstBar.EndTime != new DateTime(1998, 3, 3) || firstBar.Close != 25.11427695m)
{
throw new Exception("First History bar - unexpected data received");
}

View File

@@ -119,7 +119,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "1459983342"}
{"OrderListHash", "187652813"}
};
}
}

View File

@@ -65,32 +65,32 @@ namespace QuantConnect.Algorithm.CSharp
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "2"},
{"Total Trades", "5"},
{"Average Win", "0%"},
{"Average Loss", "-0.98%"},
{"Compounding Annual Return", "-53.792%"},
{"Drawdown", "1.500%"},
{"Average Loss", "-0.52%"},
{"Compounding Annual Return", "246.602%"},
{"Drawdown", "2.300%"},
{"Expectancy", "-1"},
{"Net Profit", "-0.982%"},
{"Sharpe Ratio", "-5.949"},
{"Probabilistic Sharpe Ratio", "1.216%"},
{"Net Profit", "1.602%"},
{"Sharpe Ratio", "8.065"},
{"Probabilistic Sharpe Ratio", "65.943%"},
{"Loss Rate", "100%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "-0.973"},
{"Beta", "0.268"},
{"Annual Standard Deviation", "0.077"},
{"Annual Variance", "0.006"},
{"Information Ratio", "-14.167"},
{"Tracking Error", "0.168"},
{"Treynor Ratio", "-1.705"},
{"Total Fees", "$6.51"},
{"Fitness Score", "0.249"},
{"Alpha", "-0.157"},
{"Beta", "1.015"},
{"Annual Standard Deviation", "0.223"},
{"Annual Variance", "0.05"},
{"Information Ratio", "-27.079"},
{"Tracking Error", "0.005"},
{"Treynor Ratio", "1.772"},
{"Total Fees", "$16.28"},
{"Fitness Score", "0.999"},
{"Kelly Criterion Estimate", "38.64"},
{"Kelly Criterion Probability Value", "0.229"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "-55.465"},
{"Portfolio Turnover", "0.498"},
{"Return Over Maximum Drawdown", "78.607"},
{"Portfolio Turnover", "1.246"},
{"Total Insights Generated", "100"},
{"Total Insights Closed", "99"},
{"Total Insights Analysis Completed", "99"},
@@ -104,7 +104,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "54.5455%"},
{"Rolling Averaged Population Direction", "59.8056%"},
{"Rolling Averaged Population Magnitude", "59.8056%"},
{"OrderListHash", "160051570"}
{"OrderListHash", "-1552239367"}
};
}
}

View File

@@ -0,0 +1,82 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Regression Algorithm for testing engine behavior with throwing errors in OnOrderEvent
/// Should result in a RunTimeError status.
/// Reference GH Issue #4947
/// </summary>
public class OnOrderEventExceptionRegression : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol _spy = QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA);
/// <summary>
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
/// </summary>
public override void Initialize()
{
SetStartDate(2013, 10, 07);
SetEndDate(2013, 10, 11);
AddEquity("SPY", Resolution.Minute);
}
/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// </summary>
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
public override void OnData(Slice data)
{
if (!Portfolio.Invested)
{
SetHoldings(_spy, 1);
Debug("Purchased Stock");
}
}
/// <summary>
/// OnOrderEvent is called whenever an order is updated
/// </summary>
/// <param name="orderEvent">Order Event</param>
public override void OnOrderEvent(OrderEvent orderEvent)
{
throw new Exception("OnOrderEvent exception");
}
/// <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>
{
};
}
}

View File

@@ -0,0 +1,122 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This regression algorithm verifies automatic option contract assignment behavior.
/// </summary>
/// <meta name="tag" content="regression test" />
/// <meta name="tag" content="options" />
/// <meta name="tag" content="using data" />
/// <meta name="tag" content="filter selection" />
public class OptionAssignmentRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Security Stock;
private Security CallOption;
private Symbol CallOptionSymbol;
private Security PutOption;
private Symbol PutOptionSymbol;
public override void Initialize()
{
SetStartDate(2015, 12, 23);
SetEndDate(2015, 12, 24);
SetCash(100000);
Stock = AddEquity("GOOG", Resolution.Minute);
var contracts = OptionChainProvider.GetOptionContractList(Stock.Symbol, UtcTime).ToList();
PutOptionSymbol = contracts
.Where(c => c.ID.OptionRight == OptionRight.Put)
.OrderBy(c => c.ID.Date)
.First(c => c.ID.StrikePrice == 800m);
CallOptionSymbol = contracts
.Where(c => c.ID.OptionRight == OptionRight.Call)
.OrderBy(c => c.ID.Date)
.First(c => c.ID.StrikePrice == 600m);
PutOption = AddOptionContract(PutOptionSymbol);
CallOption = AddOptionContract(CallOptionSymbol);
}
public override void OnData(Slice data)
{
if (!Portfolio.Invested && Stock.Price != 0 && PutOption.Price != 0 && CallOption.Price != 0)
{
// this gets executed on start and after each auto-assignment, finally ending with expiration assignment
MarketOrder(PutOptionSymbol, -1);
MarketOrder(CallOptionSymbol, -1);
}
}
public bool CanRunLocally { get; } = true;
public Language[] Languages { get; } = {Language.CSharp};
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "22"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0%"},
{"Drawdown", "0%"},
{"Expectancy", "0"},
{"Net Profit", "0%"},
{"Sharpe Ratio", "0"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0"},
{"Beta", "0"},
{"Annual Standard Deviation", "0"},
{"Annual Variance", "0"},
{"Information Ratio", "0"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$12.00"},
{"Fitness Score", "0.5"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "-50.218"},
{"Portfolio Turnover", "6.713"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
{"Long Insight Count", "0"},
{"Short Insight Count", "0"},
{"Long/Short Ratio", "100%"},
{"Estimated Monthly Alpha Value", "$0"},
{"Total Accumulated Estimated Alpha Value", "$0"},
{"Mean Population Estimated Insight Value", "$0"},
{"Mean Population Direction", "0%"},
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "-1597098916"}
};
}
}

View File

@@ -21,7 +21,7 @@ using QuantConnect.Interfaces;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Demonstration of the Option Chain Provider -- a much faster mechanism for manually specifying the option contracts you'd like to recieve
/// Demonstration of the Option Chain Provider -- a much faster mechanism for manually specifying the option contracts you'd like to receive
/// data for and manually subscribing to them.
/// </summary>
/// <meta name="tag" content="strategy example" />

View File

@@ -0,0 +1,142 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using System.Collections.Generic;
using QuantConnect.Algorithm.Framework.Selection;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Regression algorithm making sure that the added universe selection does not remove the option chain during it's daily refresh
/// </summary>
public class OptionChainedAndUniverseSelectionRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Symbol _aaplOption;
public override void Initialize()
{
UniverseSettings.Resolution = Resolution.Minute;
SetStartDate(2014, 06, 05);
SetEndDate(2014, 06, 09);
_aaplOption = AddOption("AAPL").Symbol;
AddUniverseSelection(new DailyUniverseSelectionModel("MyCustomSelectionModel", time => new[] { "AAPL" }, this));
}
public override void OnData(Slice data)
{
if (!Portfolio.Invested)
{
Buy("AAPL", 1);
}
}
public override void OnEndOfAlgorithm()
{
var config = SubscriptionManager.Subscriptions.ToList();
if (config.All(dataConfig => dataConfig.Symbol != "AAPL"))
{
throw new Exception("Was expecting configurations for AAPL");
}
if (config.All(dataConfig => dataConfig.Symbol.SecurityType != SecurityType.Option))
{
throw new Exception($"Was expecting configurations for {_aaplOption}");
}
}
private class DailyUniverseSelectionModel : CustomUniverseSelectionModel
{
private DateTime _lastRefresh;
private IAlgorithm _algorithm;
public DailyUniverseSelectionModel(string name, Func<DateTime, IEnumerable<string>> selector, IAlgorithm algorithm) : base(name, selector)
{
_algorithm = algorithm;
}
public override DateTime GetNextRefreshTimeUtc()
{
if (_lastRefresh != _algorithm.Time.Date)
{
_lastRefresh = _algorithm.Time.Date;
return DateTime.MinValue;
}
return DateTime.MaxValue;
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "1"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0.678%"},
{"Drawdown", "3.700%"},
{"Expectancy", "0"},
{"Net Profit", "0.009%"},
{"Sharpe Ratio", "7.969"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0.046"},
{"Beta", "-0.032"},
{"Annual Standard Deviation", "0.001"},
{"Annual Variance", "0"},
{"Information Ratio", "-24.461"},
{"Tracking Error", "0.044"},
{"Treynor Ratio", "-0.336"},
{"Total Fees", "$1.00"},
{"Fitness Score", "0.003"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
{"Portfolio Turnover", "0.003"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
{"Long Insight Count", "0"},
{"Short Insight Count", "0"},
{"Long/Short Ratio", "100%"},
{"Estimated Monthly Alpha Value", "$0"},
{"Total Accumulated Estimated Alpha Value", "$0"},
{"Mean Population Estimated Insight Value", "$0"},
{"Mean Population Direction", "0%"},
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "-1779427412"}
};
}
}

View File

@@ -161,7 +161,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "1038273097"}
{"OrderListHash", "-1726463684"}
};
}
}

View File

@@ -56,6 +56,13 @@ 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 dividend in slice.Dividends.Values)
{
if (dividend.ReferencePrice != 32.59m || dividend.Distribution != 3.82m)
{
throw new Exception($"{Time} - Invalid dividend {dividend}");
}
}
if (!Portfolio.Invested)
{
if (Time.Day == 28 && Time.Hour > 9 && Time.Minute > 0)
@@ -139,11 +146,11 @@ namespace QuantConnect.Algorithm.CSharp
{"Total Trades", "4"},
{"Average Win", "0%"},
{"Average Loss", "-0.02%"},
{"Compounding Annual Return", "-0.453%"},
{"Compounding Annual Return", "-0.492%"},
{"Drawdown", "0.000%"},
{"Expectancy", "-1"},
{"Net Profit", "-0.006%"},
{"Sharpe Ratio", "-3.554"},
{"Sharpe Ratio", "-3.943"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "100%"},
{"Win Rate", "0%"},
@@ -152,15 +159,15 @@ namespace QuantConnect.Algorithm.CSharp
{"Beta", "0"},
{"Annual Standard Deviation", "0.002"},
{"Annual Variance", "0"},
{"Information Ratio", "-3.554"},
{"Information Ratio", "-3.943"},
{"Tracking Error", "0.002"},
{"Treynor Ratio", "0"},
{"Total Fees", "$4.00"},
{"Fitness Score", "0.001"},
{"Fitness Score", "0"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "-1.768"},
{"Return Over Maximum Drawdown", "-2.808"},
{"Portfolio Turnover", "0.001"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},

View File

@@ -142,6 +142,31 @@
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="AddAlphaModelAlgorithm.cs" />
<Compile Include="CustomBuyingPowerModelAlgorithm.cs" />
<Compile Include="AddFutureOptionContractDataStreamingRegressionAlgorithm.cs" />
<Compile Include="AddFutureOptionSingleOptionChainSelectedInUniverseFilterRegressionAlgorithm.cs" />
<Compile Include="AddOptionContractExpiresRegressionAlgorithm.cs" />
<Compile Include="AltData\QuiverWallStreetBetsDataAlgorithm.cs" />
<Compile Include="FutureOptionCallITMGreeksExpiryRegressionAlgorithm.cs" />
<Compile Include="OnOrderEventExceptionRegression.cs" />
<Compile Include="FutureOptionCallITMExpiryRegressionAlgorithm.cs" />
<Compile Include="FutureOptionCallOTMExpiryRegressionAlgorithm.cs" />
<Compile Include="FutureOptionPutITMExpiryRegressionAlgorithm.cs" />
<Compile Include="FutureOptionPutOTMExpiryRegressionAlgorithm.cs" />
<Compile Include="FutureOptionBuySellCallIntradayRegressionAlgorithm.cs" />
<Compile Include="FutureOptionShortCallITMExpiryRegressionAlgorithm.cs" />
<Compile Include="FutureOptionShortCallOTMExpiryRegressionAlgorithm.cs" />
<Compile Include="FutureOptionShortPutOTMExpiryRegressionAlgorithm.cs" />
<Compile Include="FutureOptionShortPutITMExpiryRegressionAlgorithm.cs" />
<Compile Include="ScaledFillForwardDataRegressionAlgorithm.cs" />
<Compile Include="DailyHistoryForDailyResolutionRegressionAlgorithm.cs" />
<Compile Include="DailyHistoryForMinuteResolutionRegressionAlgorithm.cs" />
<Compile Include="ExtendedMarketHoursHistoryRegressionAlgorithm.cs" />
<Compile Include="EquityTickQuoteAdjustedModeRegressionAlgorithm.cs" />
<Compile Include="AddOptionContractFromUniverseRegressionAlgorithm.cs" />
<Compile Include="CoarseFineOptionUniverseChainRegressionAlgorithm.cs" />
<Compile Include="OptionChainedAndUniverseSelectionRegressionAlgorithm.cs" />
<Compile Include="OptionAssignmentRegressionAlgorithm.cs" />
<Compile Include="SwitchDataModeRegressionAlgorithm.cs" />
<Compile Include="AddRemoveOptionUniverseRegressionAlgorithm.cs" />
<Compile Include="AddRemoveSecurityRegressionAlgorithm.cs" />
@@ -356,6 +381,7 @@
<Compile Include="USEnergyInformationAdministrationAlgorithm.cs" />
<Compile Include="UserDefinedUniverseAlgorithm.cs" />
<Compile Include="VolumeWeightedAveragePriceExecutionModelRegressionAlgorithm.cs" />
<Compile Include="WarmUpAfterIntializeRegression.cs" />
<Compile Include="WarmupAlgorithm.cs" />
<Compile Include="WarmupConversionRatesRegressionAlgorithm.cs" />
<Compile Include="WarmupHistoryAlgorithm.cs" />
@@ -431,7 +457,7 @@
<Analyzer Include="..\packages\Microsoft.NetFramework.Analyzers.2.9.3\analyzers\dotnet\cs\Microsoft.NetFramework.CSharp.Analyzers.dll" />
</ItemGroup>
<ItemGroup>
<Compile Include="DaylightSavingTimeHistoryRegressionTest.cs" />
<Compile Include="DaylightSavingTimeHistoryRegressionAlgorithm.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

View File

@@ -0,0 +1,143 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// This regression test algorithm reproduces issue https://github.com/QuantConnect/Lean/issues/4834
/// fixed in PR https://github.com/QuantConnect/Lean/pull/4836
/// Adjusted data of fill forward bars should use original scale factor
/// </summary>
public class ScaledFillForwardDataRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private TradeBar _lastRealBar;
private Symbol _twx;
public override void Initialize()
{
SetStartDate(2014, 6, 5);
SetEndDate(2014, 6, 9);
_twx = AddEquity("TWX", Resolution.Minute, extendedMarketHours: true).Symbol;
Schedule.On(DateRules.EveryDay(_twx), TimeRules.Every(TimeSpan.FromHours(1)), PlotPrice);
}
private void PlotPrice()
{
Plot($"{_twx}", "Ask", Securities[_twx].AskPrice);
Plot($"{_twx}", "Bid", Securities[_twx].BidPrice);
Plot($"{_twx}", "Price", Securities[_twx].Price);
Plot("Portfolio.TPV", "Value", Portfolio.TotalPortfolioValue);
}
public override void OnData(Slice data)
{
var current = data.Bars.FirstOrDefault().Value;
if (current != null)
{
if (Time == new DateTime(2014, 06, 09, 4, 1, 0) && !Portfolio.Invested)
{
if (!current.IsFillForward)
{
throw new Exception($"Was expecting a first fill forward bar {Time}");
}
// trade on the first bar after a factor price scale change. +10 so we fill ASAP. Limit so it fills in extended market hours
LimitOrder(_twx, 1000, _lastRealBar.Close + 10);
}
if (_lastRealBar == null || !current.IsFillForward)
{
_lastRealBar = current;
}
else if (_lastRealBar.Close != current.Close)
{
throw new Exception($"FillForwarded data point at {Time} was scaled. Actual: {current.Close}; Expected: {_lastRealBar.Close}");
}
}
}
public override void OnEndOfAlgorithm()
{
if (_lastRealBar == null)
{
throw new Exception($"Not all expected data points were received.");
}
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "1"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "32.825%"},
{"Drawdown", "0.800%"},
{"Expectancy", "0"},
{"Net Profit", "0.377%"},
{"Sharpe Ratio", "8.953"},
{"Probabilistic Sharpe Ratio", "95.977%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0.314"},
{"Beta", "-0.104"},
{"Annual Standard Deviation", "0.03"},
{"Annual Variance", "0.001"},
{"Information Ratio", "-3.498"},
{"Tracking Error", "0.05"},
{"Treynor Ratio", "-2.573"},
{"Total Fees", "$5.00"},
{"Fitness Score", "0.158"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
{"Portfolio Turnover", "0.158"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
{"Long Insight Count", "0"},
{"Short Insight Count", "0"},
{"Long/Short Ratio", "100%"},
{"Estimated Monthly Alpha Value", "$0"},
{"Total Accumulated Estimated Alpha Value", "$0"},
{"Mean Population Estimated Insight Value", "$0"},
{"Mean Population Direction", "0%"},
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "960108217"}
};
}
}

View File

@@ -254,13 +254,13 @@ namespace QuantConnect.Algorithm.CSharp
{"Information Ratio", "0"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$48.56"},
{"Total Fees", "$48.58"},
{"Fitness Score", "0.5"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "-141.917"},
{"Portfolio Turnover", "2.001"},
{"Return Over Maximum Drawdown", "-141.877"},
{"Portfolio Turnover", "2.002"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
@@ -274,7 +274,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "-22119963"}
{"OrderListHash", "-263077697"}
};
}
}

View File

@@ -64,7 +64,7 @@ namespace QuantConnect.Algorithm.CSharp
{
if (_expectedCloseValues.Count > 0)
{
throw new Exception($"Not all expected data points were recieved.");
throw new Exception($"Not all expected data points were received.");
}
}

View File

@@ -189,7 +189,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "-569072921"}
{"OrderListHash", "359885308"}
};
}
}

View File

@@ -117,28 +117,28 @@ namespace QuantConnect.Algorithm.CSharp
{"Total Trades", "3528"},
{"Average Win", "0.67%"},
{"Average Loss", "-0.71%"},
{"Compounding Annual Return", "17.318%"},
{"Compounding Annual Return", "17.227%"},
{"Drawdown", "63.700%"},
{"Expectancy", "0.020"},
{"Net Profit", "17.318%"},
{"Sharpe Ratio", "0.836"},
{"Probabilistic Sharpe Ratio", "33.715%"},
{"Net Profit", "17.227%"},
{"Sharpe Ratio", "0.834"},
{"Probabilistic Sharpe Ratio", "33.688%"},
{"Loss Rate", "48%"},
{"Win Rate", "52%"},
{"Profit-Loss Ratio", "0.95"},
{"Alpha", "0.826"},
{"Alpha", "0.825"},
{"Beta", "-0.34"},
{"Annual Standard Deviation", "0.945"},
{"Annual Variance", "0.893"},
{"Information Ratio", "0.714"},
{"Information Ratio", "0.713"},
{"Tracking Error", "0.957"},
{"Treynor Ratio", "-2.325"},
{"Total Fees", "$24713.42"},
{"Treynor Ratio", "-2.323"},
{"Total Fees", "$24760.85"},
{"Fitness Score", "0.54"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "0.24"},
{"Return Over Maximum Drawdown", "0.272"},
{"Sortino Ratio", "0.238"},
{"Return Over Maximum Drawdown", "0.27"},
{"Portfolio Turnover", "7.204"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
@@ -153,7 +153,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "-1547947497"}
{"OrderListHash", "843493486"}
};
}
}

View File

@@ -172,32 +172,32 @@ namespace QuantConnect.Algorithm.CSharp
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "5"},
{"Total Trades", "4"},
{"Average Win", "0.64%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "-74.197%"},
{"Drawdown", "6.600%"},
{"Compounding Annual Return", "-56.577%"},
{"Drawdown", "3.800%"},
{"Expectancy", "0"},
{"Net Profit", "-6.115%"},
{"Sharpe Ratio", "-2.281"},
{"Probabilistic Sharpe Ratio", "11.870%"},
{"Net Profit", "-3.811%"},
{"Sharpe Ratio", "-2.773"},
{"Probabilistic Sharpe Ratio", "13.961%"},
{"Loss Rate", "0%"},
{"Win Rate", "100%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "-0.684"},
{"Beta", "-0.113"},
{"Annual Standard Deviation", "0.292"},
{"Annual Variance", "0.085"},
{"Information Ratio", "-1.606"},
{"Tracking Error", "0.312"},
{"Treynor Ratio", "5.866"},
{"Total Fees", "$5.00"},
{"Fitness Score", "0.017"},
{"Alpha", "-0.504"},
{"Beta", "-0.052"},
{"Annual Standard Deviation", "0.179"},
{"Annual Variance", "0.032"},
{"Information Ratio", "-1.599"},
{"Tracking Error", "0.207"},
{"Treynor Ratio", "9.508"},
{"Total Fees", "$4.00"},
{"Fitness Score", "0.008"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "-2.584"},
{"Return Over Maximum Drawdown", "-11.287"},
{"Portfolio Turnover", "0.177"},
{"Sortino Ratio", "-3.791"},
{"Return Over Maximum Drawdown", "-14.846"},
{"Portfolio Turnover", "0.136"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
@@ -211,7 +211,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "-1386253041"}
{"OrderListHash", "1484950465"}
};
}
}

View File

@@ -32,6 +32,7 @@ namespace QuantConnect.Algorithm.CSharp
{
private Symbol _spy;
private int _reselectedSpy = -1;
private DateTime lastDataTime = DateTime.MinValue;
/// <summary>
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
@@ -57,6 +58,13 @@ namespace QuantConnect.Algorithm.CSharp
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
public override void OnData(Slice data)
{
if (lastDataTime == data.Time)
{
throw new Exception("Duplicate time for current data and last data slice");
}
lastDataTime = data.Time;
if (_reselectedSpy == 0)
{
if (!Securities[_spy].IsTradable)
@@ -111,29 +119,29 @@ namespace QuantConnect.Algorithm.CSharp
{"Total Trades", "1"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "75.079%"},
{"Drawdown", "2.200%"},
{"Compounding Annual Return", "69.904%"},
{"Drawdown", "2.000%"},
{"Expectancy", "0"},
{"Net Profit", "4.711%"},
{"Sharpe Ratio", "5.067"},
{"Probabilistic Sharpe Ratio", "84.391%"},
{"Net Profit", "4.453%"},
{"Sharpe Ratio", "4.805"},
{"Probabilistic Sharpe Ratio", "83.459%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0.562"},
{"Beta", "0.02"},
{"Annual Standard Deviation", "0.113"},
{"Annual Variance", "0.013"},
{"Information Ratio", "0.511"},
{"Tracking Error", "0.159"},
{"Treynor Ratio", "28.945"},
{"Total Fees", "$3.22"},
{"Fitness Score", "0.037"},
{"Alpha", "0.501"},
{"Beta", "0.068"},
{"Annual Standard Deviation", "0.111"},
{"Annual Variance", "0.012"},
{"Information Ratio", "0.284"},
{"Tracking Error", "0.153"},
{"Treynor Ratio", "7.844"},
{"Total Fees", "$3.23"},
{"Fitness Score", "0.038"},
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "17.868"},
{"Return Over Maximum Drawdown", "34.832"},
{"Portfolio Turnover", "0.037"},
{"Sortino Ratio", "16.857"},
{"Return Over Maximum Drawdown", "34.897"},
{"Portfolio Turnover", "0.038"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
@@ -147,7 +155,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "836605283"}
{"OrderListHash", "1664042885"}
};
}
}

View File

@@ -0,0 +1,61 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Collections.Generic;
using QuantConnect.Data;
using QuantConnect.Interfaces;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Regression algorithm to test warming up after initialize behavior, should throw if used outside of initialize
/// Reference GH Issue #4939
/// </summary>
public class WarmUpAfterIntializeRegression : QCAlgorithm, IRegressionAlgorithmDefinition
{
public override void Initialize()
{
SetStartDate(2013, 10, 07); //Set Start Date
SetEndDate(2013, 10, 11); //Set End Date
SetCash(100000);
var equity = AddEquity("SPY");
}
public override void OnData(Slice slice)
{
// Should throw and set Algorithm status to be runtime error
SetWarmUp(TimeSpan.FromDays(2));
}
/// <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>
{
};
}
}

View File

@@ -105,8 +105,8 @@ namespace QuantConnect.Algorithm.CSharp
{"Kelly Criterion Estimate", "0"},
{"Kelly Criterion Probability Value", "0"},
{"Sortino Ratio", "79228162514264337593543950335"},
{"Return Over Maximum Drawdown", "-315.532"},
{"Portfolio Turnover", "0.998"},
{"Return Over Maximum Drawdown", "-315.48"},
{"Portfolio Turnover", "0.999"},
{"Total Insights Generated", "0"},
{"Total Insights Closed", "0"},
{"Total Insights Analysis Completed", "0"},
@@ -120,7 +120,7 @@ namespace QuantConnect.Algorithm.CSharp
{"Mean Population Magnitude", "0%"},
{"Rolling Averaged Population Direction", "0%"},
{"Rolling Averaged Population Magnitude", "0%"},
{"OrderListHash", "1318619937"}
{"OrderListHash", "1703396395"}
};
}
}

View File

@@ -67,9 +67,11 @@ namespace QuantConnect.Algorithm.Framework.Risk
}
var pnl = GetTotalDrawdownPercent(currentValue);
if (pnl < _maximumDrawdownPercent)
if (pnl < _maximumDrawdownPercent && targets.Length != 0)
{
foreach(var target in targets)
// reset the trailing high value for restart investing on next rebalcing period
_initialised = false;
foreach (var target in targets)
yield return new PortfolioTarget(target.Symbol, 0);
}
}

View File

@@ -54,10 +54,11 @@ class MaximumDrawdownPercentPortfolio(RiskManagementModel):
return [] # return if new high reached
pnl = self.GetTotalDrawdownPercent(currentValue)
if pnl < self.maximumDrawdownPercent:
if pnl < self.maximumDrawdownPercent and len(targets) != 0:
self.initialised = False # reset the trailing high value for restart investing on next rebalcing period
return [ PortfolioTarget(target.Symbol, 0) for target in targets ]
return []
def GetTotalDrawdownPercent(self, currentValue):
return (float(currentValue) / float(self.portfolioHigh)) - 1.0
return (float(currentValue) / float(self.portfolioHigh)) - 1.0

View File

@@ -61,7 +61,7 @@ namespace QuantConnect.Algorithm.Framework.Selection
/// </summary>
protected override FutureFilterUniverse Filter(FutureFilterUniverse filter)
{
return filter.Contracts(FilterByOpenInterest(filter.ToDictionary(x => x, x => _marketHoursDatabase.GetEntry(x.ID.Market, x, x.ID.SecurityType).ExchangeHours)));
return filter.Contracts(FilterByOpenInterest(filter.ToDictionary(x => x, x => _marketHoursDatabase.GetEntry(x.ID.Market, x, x.ID.SecurityType))));
}
/// <summary>
@@ -69,7 +69,7 @@ namespace QuantConnect.Algorithm.Framework.Selection
/// </summary>
/// <param name="contracts">Contracts to filter</param>
/// <returns>Filtered set</returns>
public IEnumerable<Symbol> FilterByOpenInterest(IReadOnlyDictionary<Symbol, SecurityExchangeHours> contracts)
public IEnumerable<Symbol> FilterByOpenInterest(IReadOnlyDictionary<Symbol, MarketHoursDatabase.Entry> contracts)
{
var symbols = new List<Symbol>(_chainContractsLookupLimit.HasValue ? contracts.Keys.OrderBy(x => x.ID.Date).Take(_chainContractsLookupLimit.Value) : contracts.Keys);
var openInterest = symbols.GroupBy(x => contracts[x]).SelectMany(g => GetOpenInterest(g.Key, g.Select(i => i))).ToDictionary(x => x.Key, x => x.Value);
@@ -91,11 +91,12 @@ namespace QuantConnect.Algorithm.Framework.Selection
return filtered;
}
private Dictionary<Symbol, decimal> GetOpenInterest(SecurityExchangeHours exchangeHours, IEnumerable<Symbol> symbols)
private Dictionary<Symbol, decimal> GetOpenInterest(MarketHoursDatabase.Entry marketHours, IEnumerable<Symbol> symbols)
{
var current = _algorithm.UtcTime;
var exchangeHours = marketHours.ExchangeHours;
var endTime = Instant.FromDateTimeUtc(_algorithm.UtcTime).InZone(exchangeHours.TimeZone).ToDateTimeUnspecified();
var previousDay = Time.GetStartTimeForTradeBars(exchangeHours, endTime, Time.OneDay, 1, true);
var previousDay = Time.GetStartTimeForTradeBars(exchangeHours, endTime, Time.OneDay, 1, true, marketHours.DataTimeZone);
var requests = symbols.Select(
symbol => new HistoryRequest(
previousDay,

View File

@@ -15,12 +15,9 @@
using System;
using System.Collections.Generic;
using QuantConnect.Data;
using QuantConnect.Data.Auxiliary;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
using QuantConnect.Securities.Option;
namespace QuantConnect.Algorithm.Framework.Selection
{
@@ -99,61 +96,19 @@ namespace QuantConnect.Algorithm.Framework.Selection
var uniqueUnderlyingSymbols = new HashSet<Symbol>();
foreach (var optionSymbol in _optionChainSymbolSelector(algorithm.UtcTime))
{
if (optionSymbol.SecurityType != SecurityType.Option)
if (optionSymbol.SecurityType != SecurityType.Option && optionSymbol.SecurityType != SecurityType.FutureOption)
{
throw new ArgumentException("optionChainSymbolSelector must return option symbols.");
throw new ArgumentException("optionChainSymbolSelector must return option or futures options symbols.");
}
// prevent creating duplicate option chains -- one per underlying
if (uniqueUnderlyingSymbols.Add(optionSymbol.Underlying))
{
yield return CreateOptionChain(algorithm, optionSymbol);
yield return algorithm.CreateOptionChain(optionSymbol, Filter, _universeSettings);
}
}
}
/// <summary>
/// Creates the canonical <see cref="Option"/> chain security for a given symbol
/// </summary>
/// <param name="algorithm">The algorithm instance to create universes for</param>
/// <param name="symbol">Symbol of the option</param>
/// <param name="settings">Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed</param>
/// <param name="initializer">Performs extra initialization (such as setting models) after we create a new security object</param>
/// <returns><see cref="Option"/> for the given symbol</returns>
[Obsolete("This method is obsolete because SecurityInitializer is obsolete and will not be used.")]
protected virtual Option CreateOptionChainSecurity(QCAlgorithm algorithm, Symbol symbol, UniverseSettings settings, ISecurityInitializer initializer)
{
return CreateOptionChainSecurity(
algorithm.SubscriptionManager.SubscriptionDataConfigService,
symbol,
settings,
algorithm.Securities);
}
/// <summary>
/// Creates the canonical <see cref="Option"/> chain security for a given symbol
/// </summary>
/// <param name="subscriptionDataConfigService">The service used to create new <see cref="SubscriptionDataConfig"/></param>
/// <param name="symbol">Symbol of the option</param>
/// <param name="settings">Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed</param>
/// <param name="securityManager">Used to create new <see cref="Security"/></param>
/// <returns><see cref="Option"/> for the given symbol</returns>
protected virtual Option CreateOptionChainSecurity(
ISubscriptionDataConfigService subscriptionDataConfigService,
Symbol symbol,
UniverseSettings settings,
SecurityManager securityManager)
{
var config = subscriptionDataConfigService.Add(
typeof(ZipEntryName),
symbol,
settings.Resolution,
settings.FillForward,
settings.ExtendedMarketHours,
false);
return (Option)securityManager.CreateSecurity(symbol, config, settings.Leverage, false);
}
/// <summary>
/// Defines the option chain universe filter
/// </summary>
@@ -162,55 +117,5 @@ namespace QuantConnect.Algorithm.Framework.Selection
// NOP
return filter;
}
/// <summary>
/// Creates a <see cref="OptionChainUniverse"/> for a given symbol
/// </summary>
/// <param name="algorithm">The algorithm instance to create universes for</param>
/// <param name="symbol">Symbol of the option</param>
/// <returns><see cref="OptionChainUniverse"/> for the given symbol</returns>
private OptionChainUniverse CreateOptionChain(QCAlgorithm algorithm, Symbol symbol)
{
if (symbol.SecurityType != SecurityType.Option)
{
throw new ArgumentException("CreateOptionChain requires an option symbol.");
}
// rewrite non-canonical symbols to be canonical
var market = symbol.ID.Market;
var underlying = symbol.Underlying;
if (!symbol.IsCanonical())
{
var alias = $"?{underlying.Value}";
symbol = Symbol.Create(underlying.Value, SecurityType.Option, market, alias);
}
// resolve defaults if not specified
var settings = _universeSettings ?? algorithm.UniverseSettings;
// create canonical security object, but don't duplicate if it already exists
Security security;
Option optionChain;
if (!algorithm.Securities.TryGetValue(symbol, out security))
{
optionChain = CreateOptionChainSecurity(
algorithm.SubscriptionManager.SubscriptionDataConfigService,
symbol,
settings,
algorithm.Securities);
}
else
{
optionChain = (Option)security;
}
// set the option chain contract filter function
optionChain.SetFilter(Filter);
// force option chain security to not be directly tradable AFTER it's configured to ensure it's not overwritten
optionChain.IsTradable = false;
return new OptionChainUniverse(optionChain, settings, algorithm.LiveMode);
}
}
}
}

View File

@@ -58,8 +58,8 @@ class OptionUniverseSelectionModel(UniverseSelectionModel):
uniqueUnderlyingSymbols = set()
for optionSymbol in self.optionChainSymbolSelector(algorithm.UtcTime):
if optionSymbol.SecurityType != SecurityType.Option:
raise ValueError("optionChainSymbolSelector must return option symbols.")
if optionSymbol.SecurityType != SecurityType.Option and optionSymbol.SecurityType != SecurityType.FutureOption:
raise ValueError("optionChainSymbolSelector must return option or futures options symbols.")
# prevent creating duplicate option chains -- one per underlying
if optionSymbol.Underlying not in uniqueUnderlyingSymbols:
@@ -73,7 +73,7 @@ class OptionUniverseSelectionModel(UniverseSelectionModel):
symbol: Symbol of the option
Returns:
OptionChainUniverse for the given symbol'''
if symbol.SecurityType != SecurityType.Option:
if symbol.SecurityType != SecurityType.Option and symbol.SecurityType != SecurityType.FutureOption:
raise ValueError("CreateOptionChain requires an option symbol.")
# rewrite non-canonical symbols to be canonical
@@ -122,4 +122,4 @@ class OptionUniverseSelectionModel(UniverseSelectionModel):
def Filter(self, filter):
'''Defines the option chain universe filter'''
# NOP
return filter
return filter

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/stubs" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
</project>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Algorithm.Python.iml" filepath="$PROJECT_DIR$/.idea/Algorithm.Python.iml" />
</modules>
</component>
</project>

View File

@@ -1,5 +0,0 @@
{
"python.autoComplete.extraPaths": [
"stubs"
]
}

View File

@@ -0,0 +1,103 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from datetime import datetime, timedelta
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Future import *
from QuantConnect import Market
### <summary>
### This regression algorithm tests that we receive the expected data when
### we add future option contracts individually using <see cref="AddFutureOptionContract"/>
### </summary>
class AddFutureOptionContractDataStreamingRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
self.onDataReached = False
self.invested = False
self.symbolsReceived = []
self.expectedSymbolsReceived = []
self.dataReceived = {}
self.SetStartDate(2020, 1, 5)
self.SetEndDate(2020, 1, 6)
self.es20h20 = self.AddFutureContract(
Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, datetime(2020, 3, 20)),
Resolution.Minute).Symbol
self.es19m20 = self.AddFutureContract(
Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, datetime(2020, 6, 19)),
Resolution.Minute).Symbol
optionChains = self.OptionChainProvider.GetOptionContractList(self.es20h20, self.Time)
optionChains += self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time)
for optionContract in optionChains:
self.expectedSymbolsReceived.append(self.AddFutureOptionContract(optionContract, Resolution.Minute).Symbol)
def OnData(self, data: Slice):
if not data.HasData:
return
self.onDataReached = True
hasOptionQuoteBars = False
for qb in data.QuoteBars.Values:
if qb.Symbol.SecurityType != SecurityType.FutureOption:
continue
hasOptionQuoteBars = True
self.symbolsReceived.append(qb.Symbol)
if qb.Symbol not in self.dataReceived:
self.dataReceived[qb.Symbol] = []
self.dataReceived[qb.Symbol].append(qb)
if self.invested or not hasOptionQuoteBars:
return
if data.ContainsKey(self.es20h20) and data.ContainsKey(self.es19m20):
self.SetHoldings(self.es20h20, 0.2)
self.SetHoldings(self.es19m20, 0.2)
self.invested = True
def OnEndOfAlgorithm(self):
super().OnEndOfAlgorithm()
self.symbolsReceived = list(set(self.symbolsReceived))
self.expectedSymbolsReceived = list(set(self.expectedSymbolsReceived))
if not self.onDataReached:
raise AssertionError("OnData() was never called.")
if len(self.symbolsReceived) != len(self.expectedSymbolsReceived):
raise AssertionError(f"Expected {len(self.expectedSymbolsReceived)} option contracts Symbols, found {len(self.symbolsReceived)}")
missingSymbols = [expectedSymbol for expectedSymbol in self.expectedSymbolsReceived if expectedSymbol not in self.symbolsReceived]
if any(missingSymbols):
raise AssertionError(f'Symbols: "{", ".join(missingSymbols)}" were not found in OnData')
for expectedSymbol in self.expectedSymbolsReceived:
data = self.dataReceived[expectedSymbol]
for dataPoint in data:
dataPoint.EndTime = datetime(1970, 1, 1)
nonDupeDataCount = len(set(data))
if nonDupeDataCount < 1000:
raise AssertionError(f"Received too few data points. Expected >=1000, found {nonDupeDataCount} for {expectedSymbol}")

View File

@@ -0,0 +1,127 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from datetime import datetime, timedelta
from QuantConnect.Algorithm import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Future import *
from QuantConnect import *
### <summary>
### This regression algorithm tests that we only receive the option chain for a single future contract
### in the option universe filter.
### </summary>
class AddFutureOptionSingleOptionChainSelectedInUniverseFilterRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
self.invested = False
self.onDataReached = False
self.optionFilterRan = False
self.symbolsReceived = []
self.expectedSymbolsReceived = []
self.dataReceived = {}
self.SetStartDate(2020, 1, 5)
self.SetEndDate(2020, 1, 6)
self.es = self.AddFuture(Futures.Indices.SP500EMini, Resolution.Minute, Market.CME)
self.es.SetFilter(lambda futureFilter: futureFilter.Expiration(0, 365).ExpirationCycle([3, 6]))
self.AddFutureOption(self.es.Symbol, self.OptionContractUniverseFilterFunction)
def OptionContractUniverseFilterFunction(self, optionContracts: OptionFilterUniverse) -> OptionFilterUniverse:
self.optionFilterRan = True
expiry = list(set([x.Underlying.ID.Date for x in optionContracts]))
expiry = None if not any(expiry) else expiry[0]
symbol = [x.Underlying for x in optionContracts]
symbol = None if not any(symbol) else symbol[0]
if expiry is None or symbol is None:
raise AssertionError("Expected a single Option contract in the chain, found 0 contracts")
enumerator = optionContracts.GetEnumerator()
while enumerator.MoveNext():
self.expectedSymbolsReceived.append(enumerator.Current)
return optionContracts
def OnData(self, data: Slice):
if not data.HasData:
return
self.onDataReached = True
hasOptionQuoteBars = False
for qb in data.QuoteBars.Values:
if qb.Symbol.SecurityType != SecurityType.FutureOption:
continue
hasOptionQuoteBars = True
self.symbolsReceived.append(qb.Symbol)
if qb.Symbol not in self.dataReceived:
self.dataReceived[qb.Symbol] = []
self.dataReceived[qb.Symbol].append(qb)
if self.invested or not hasOptionQuoteBars:
return
for chain in data.OptionChains.Values:
futureInvested = False
optionInvested = False
for option in chain.Contracts.Keys:
if futureInvested and optionInvested:
return
future = option.Underlying
if not optionInvested and data.ContainsKey(option):
self.MarketOrder(option, 1)
self.invested = True
optionInvested = True
if not futureInvested and data.ContainsKey(future):
self.MarketOrder(future, 1)
self.invested = True
futureInvested = True
def OnEndOfAlgorithm(self):
super().OnEndOfAlgorithm()
self.symbolsReceived = list(set(self.symbolsReceived))
self.expectedSymbolsReceived = list(set(self.expectedSymbolsReceived))
if not self.optionFilterRan:
raise AssertionError("Option chain filter was never ran")
if not self.onDataReached:
raise AssertionError("OnData() was never called.")
if len(self.symbolsReceived) != len(self.expectedSymbolsReceived):
raise AssertionError(f"Expected {len(self.expectedSymbolsReceived)} option contracts Symbols, found {len(self.symbolsReceived)}")
missingSymbols = [expectedSymbol for expectedSymbol in self.expectedSymbolsReceived if expectedSymbol not in self.symbolsReceived]
if any(missingSymbols):
raise AssertionError(f'Symbols: "{", ".join(missingSymbols)}" were not found in OnData')
for expectedSymbol in self.expectedSymbolsReceived:
data = self.dataReceived[expectedSymbol]
for dataPoint in data:
dataPoint.EndTime = datetime(1970, 1, 1)
nonDupeDataCount = len(set(data))
if nonDupeDataCount < 1000:
raise AssertionError(f"Received too few data points. Expected >=1000, found {nonDupeDataCount} for {expectedSymbol}")

View File

@@ -0,0 +1,67 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from datetime import *
### <summary>
### We add an option contract using 'QCAlgorithm.AddOptionContract' and place a trade, the underlying
### gets deselected from the universe selection but should still be present since we manually added the option contract.
### Later we call 'QCAlgorithm.RemoveOptionContract' and expect both option and underlying to be removed.
### </summary>
class AddOptionContractExpiresRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.SetStartDate(2014, 6, 5)
self.SetEndDate(2014, 6, 30)
self._expiration = datetime(2014, 6, 21)
self._option = None
self._traded = False
self._twx = Symbol.Create("TWX", SecurityType.Equity, Market.USA)
self.AddUniverse("my-daily-universe-name", self.Selector)
def Selector(self, time):
return [ "AAPL" ]
def OnData(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
if self._option == None:
options = self.OptionChainProvider.GetOptionContractList(self._twx, self.Time)
options = sorted(options, key=lambda x: x.ID.Symbol)
option = next((option for option in options if option.ID.Date == self._expiration and option.ID.OptionRight == OptionRight.Call and option.ID.OptionStyle == OptionStyle.American), None)
if option != None:
self._option = self.AddOptionContract(option).Symbol;
if self._option != None and self.Securities[self._option].Price != 0 and not self._traded:
self._traded = True;
self.Buy(self._option, 1);
if self.Time > self._expiration and self.Securities[self._twx].Invested:
# we liquidate the option exercised position
self.Liquidate(self._twx);

View File

@@ -0,0 +1,87 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from datetime import *
### <summary>
### We add an option contract using 'QCAlgorithm.AddOptionContract' and place a trade, the underlying
### gets deselected from the universe selection but should still be present since we manually added the option contract.
### Later we call 'QCAlgorithm.RemoveOptionContract' and expect both option and underlying to be removed.
### </summary>
class AddOptionContractFromUniverseRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.SetStartDate(2014, 6, 5)
self.SetEndDate(2014, 6, 9)
self._expiration = datetime(2014, 6, 21)
self._securityChanges = None
self._option = None
self._traded = False
self._twx = Symbol.Create("TWX", SecurityType.Equity, Market.USA)
self._aapl = Symbol.Create("AAPL", SecurityType.Equity, Market.USA)
self.UniverseSettings.Resolution = Resolution.Minute
self.UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw
self.AddUniverse(self.Selector, self.Selector)
def Selector(self, fundamental):
if self.Time <= datetime(2014, 6, 5):
return [ self._twx ]
return [ self._aapl ]
def OnData(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
if self._option != None and self.Securities[self._option].Price != 0 and not self._traded:
self._traded = True;
self.Buy(self._option, 1);
if self.Time == datetime(2014, 6, 6, 14, 0, 0):
# liquidate & remove the option
self.RemoveOptionContract(self._option)
def OnSecuritiesChanged(self, changes):
# keep track of all removed and added securities
if self._securityChanges == None:
self._securityChanges = changes
else:
self._securityChanges.op_Addition(self._securityChanges, changes)
if any(security.Symbol.SecurityType == SecurityType.Option for security in changes.AddedSecurities):
return
for addedSecurity in changes.AddedSecurities:
options = self.OptionChainProvider.GetOptionContractList(addedSecurity.Symbol, self.Time)
options = sorted(options, key=lambda x: x.ID.Symbol)
option = next((option for option in options if option.ID.Date == self._expiration and option.ID.OptionRight == OptionRight.Call and option.ID.OptionStyle == OptionStyle.American), None)
self.AddOptionContract(option)
# just keep the first we got
if self._option == None:
self._option = option

View File

@@ -0,0 +1,50 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data import *
from QuantConnect.Data.Custom.Quiver import *
### <summary>
### Quiver Quantitative is a provider of alternative data.
### This algorithm shows how to consume the 'QuiverWallStreetBets'
### </summary>
class QuiverWallStreetBetsDataAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2019, 1, 1)
self.SetEndDate(2020, 6, 1)
self.SetCash(100000)
aapl = self.AddEquity("AAPL", Resolution.Daily).Symbol
quiverWSBSymbol = self.AddData(QuiverWallStreetBets, aapl).Symbol
history = self.History(QuiverWallStreetBets, quiverWSBSymbol, 60, Resolution.Daily)
self.Debug(f"We got {len(history)} items from our history request");
def OnData(self, data):
points = data.Get(QuiverWallStreetBets)
for point in points.Values:
# Go long in the stock if it was mentioned more than 5 times in the WallStreetBets daily discussion
if point.Mentions > 5:
self.SetHoldings(point.Symbol.Underlying, 1)
# Go short in the stock if it was mentioned less than 5 times in the WallStreetBets daily discussion
if point.Mentions < 5:
self.SetHoldings(point.Symbol.Underlying, -1)

View File

@@ -0,0 +1,99 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from clr import AddReference
AddReference("System.Core")
AddReference("System.Collections")
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Algorithm")
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data.UniverseSelection import *
from datetime import *
### <summary>
### Demonstration of how to chain a coarse and fine universe selection with an option chain universe selection model
### that will add and remove an'OptionChainUniverse' for each symbol selected on fine
### </summary>
class CoarseFineOptionUniverseChainRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.SetStartDate(2014,6,5) #Set Start Date
self.SetEndDate(2014,6,6) #Set End Date
self.UniverseSettings.Resolution = Resolution.Minute
self._twx = Symbol.Create("TWX", SecurityType.Equity, Market.USA)
self._aapl = Symbol.Create("AAPL", SecurityType.Equity, Market.USA)
self._lastEquityAdded = None
self._changes = None
self._optionCount = 0
universe = self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
self.AddUniverseOptions(universe, self.OptionFilterFunction)
def OptionFilterFunction(self, universe):
universe.IncludeWeeklys().FrontMonth()
contracts = list()
for symbol in universe:
if len(contracts) == 5:
break
contracts.append(symbol)
return universe.Contracts(contracts)
def CoarseSelectionFunction(self, coarse):
if self.Time <= datetime(2014,6,5):
return [ self._twx ]
return [ self._aapl ]
def FineSelectionFunction(self, fine):
if self.Time <= datetime(2014,6,5):
return [ self._twx ]
return [ self._aapl ]
def OnData(self, data):
if self._changes == None or any(security.Price == 0 for security in self._changes.AddedSecurities):
return
# liquidate removed securities
for security in self._changes.RemovedSecurities:
if security.Invested:
self.Liquidate(security.Symbol);
for security in self._changes.AddedSecurities:
if not security.Symbol.HasUnderlying:
self._lastEquityAdded = security.Symbol;
else:
# options added should all match prev added security
if security.Symbol.Underlying != self._lastEquityAdded:
raise ValueError(f"Unexpected symbol added {security.Symbol}")
self._optionCount += 1
self.SetHoldings(security.Symbol, 0.05)
self._changes = None
# this event fires whenever we have changes to our universe
def OnSecuritiesChanged(self, changes):
if self._changes == None:
self._changes = changes
return
self._changes = self._changes.op_Addition(self._changes, changes)
def OnEndOfAlgorithm(self):
if self._optionCount == 0:
raise ValueError("Option universe chain did not add any option!")

View File

@@ -22,7 +22,6 @@ from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *
from QuantConnect.Securities import *
from QuantConnect.Data.Market import *
from QuantConnect.Data.Consolidators import *
from CustomDataRegressionAlgorithm import Bitcoin
from datetime import timedelta

View File

@@ -0,0 +1,65 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Securities import *
import numpy as np
### <summary>
### Demonstration of using custom buying power model in backtesting.
### QuantConnect allows you to model all orders as deeply and accurately as you need.
### </summary>
### <meta name="tag" content="trading and orders" />
### <meta name="tag" content="transaction fees and slippage" />
### <meta name="tag" content="custom buying power models" />
class CustomBuyingPowerModelAlgorithm(QCAlgorithm):
'''Demonstration of using custom buying power model in backtesting.
QuantConnect allows you to model all orders as deeply and accurately as you need.'''
def Initialize(self):
self.SetStartDate(2013,10,1) # Set Start Date
self.SetEndDate(2013,10,31) # Set End Date
security = self.AddEquity("SPY", Resolution.Hour)
self.spy = security.Symbol
# set the buying power model
security.SetBuyingPowerModel(CustomBuyingPowerModel())
def OnData(self, slice):
if self.Portfolio.Invested:
return
quantity = self.CalculateOrderQuantity(self.spy, 1)
if quantity % 100 != 0:
raise Exception(f'CustomBuyingPowerModel only allow quantity that is multiple of 100 and {quantity} was found')
# We normally get insufficient buying power model, but the
# CustomBuyingPowerModel always says that there is sufficient buying power for the orders
self.MarketOrder(self.spy, quantity * 10)
class CustomBuyingPowerModel(BuyingPowerModel):
def GetMaximumOrderQuantityForTargetBuyingPower(self, parameters):
quantity = super().GetMaximumOrderQuantityForTargetBuyingPower(parameters).Quantity
quantity = np.floor(quantity / 100) * 100
return GetMaximumOrderQuantityResult(quantity)
def HasSufficientBuyingPowerForOrder(self, parameters):
return HasSufficientBuyingPowerForOrderResult(True)

View File

@@ -13,19 +13,18 @@
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Indicators")
AddReference("QuantConnect.Common")
from System import *
from QuantConnect import *
from QuantConnect.Python import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework.Selection import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Data.Consolidators import *
from QuantConnect.Indicators import *
from QuantConnect.Data.Market import *
from System import *
from datetime import *
class CustomConsolidatorRegressionAlgorithm(QCAlgorithm):

View File

@@ -21,6 +21,7 @@ from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework.Selection import *
from QuantConnect.Data import *
from QuantConnect.Data.Custom import *
from QuantConnect.Data.Custom.SEC import *
from QuantConnect.Data.UniverseSelection import *

View File

@@ -27,16 +27,17 @@ import numpy as np
import random
### <summary>
### Demonstration of using custom fee, slippage and fill models for modelling transactions in backtesting.
### Demonstration of using custom fee, slippage, fill, and buying power models for modelling transactions in backtesting.
### QuantConnect allows you to model all orders as deeply and accurately as you need.
### </summary>
### <meta name="tag" content="trading and orders" />
### <meta name="tag" content="transaction fees and slippage" />
### <meta name="tag" content="custom buying power models" />
### <meta name="tag" content="custom transaction models" />
### <meta name="tag" content="custom slippage models" />
### <meta name="tag" content="custom fee models" />
class CustomModelsAlgorithm(QCAlgorithm):
'''Demonstration of using custom fee, slippage and fill models for modelling transactions in backtesting.
'''Demonstration of using custom fee, slippage, fill, and buying power models for modelling transactions in backtesting.
QuantConnect allows you to model all orders as deeply and accurately as you need.'''
def Initialize(self):
@@ -49,6 +50,7 @@ class CustomModelsAlgorithm(QCAlgorithm):
self.security.SetFeeModel(CustomFeeModel(self))
self.security.SetFillModel(CustomFillModel(self))
self.security.SetSlippageModel(CustomSlippageModel(self))
self.security.SetBuyingPowerModel(CustomBuyingPowerModel(self))
def OnData(self, data):
@@ -57,12 +59,12 @@ class CustomModelsAlgorithm(QCAlgorithm):
if self.Time.day > 10 and self.security.Holdings.Quantity <= 0:
quantity = self.CalculateOrderQuantity(self.spy, .5)
self.Log("MarketOrder: " + str(quantity))
self.Log(f"MarketOrder: {quantity}")
self.MarketOrder(self.spy, quantity, True) # async needed for partial fill market orders
elif self.Time.day > 20 and self.security.Holdings.Quantity >= 0:
quantity = self.CalculateOrderQuantity(self.spy, -.5)
self.Log("MarketOrder: " + str(quantity))
self.Log(f"MarketOrder: {quantity}")
self.MarketOrder(self.spy, quantity, True) # async needed for partial fill market orders
# If we want to use methods from other models, you need to inherit from one of them
@@ -90,7 +92,7 @@ class CustomFillModel(ImmediateFillModel):
absoluteRemaining = absoluteRemaining - absoluteFillQuantity
self.absoluteRemainingByOrderId[order.Id] = absoluteRemaining
fill.Status = OrderStatus.PartiallyFilled
self.algorithm.Log("CustomFillModel: " + str(fill))
self.algorithm.Log(f"CustomFillModel: {fill}")
return fill
class CustomFeeModel(FeeModel):
@@ -102,7 +104,7 @@ class CustomFeeModel(FeeModel):
fee = max(1, parameters.Security.Price
* parameters.Order.AbsoluteQuantity
* 0.00001)
self.algorithm.Log("CustomFeeModel: " + str(fee))
self.algorithm.Log(f"CustomFeeModel: {fee}")
return OrderFee(CashAmount(fee, "USD"))
class CustomSlippageModel:
@@ -112,5 +114,15 @@ class CustomSlippageModel:
def GetSlippageApproximation(self, asset, order):
# custom slippage math
slippage = asset.Price * 0.0001 * np.log10(2*float(order.AbsoluteQuantity))
self.algorithm.Log("CustomSlippageModel: " + str(slippage))
return slippage
self.algorithm.Log(f"CustomSlippageModel: {slippage}")
return slippage
class CustomBuyingPowerModel(BuyingPowerModel):
def __init__(self, algorithm):
self.algorithm = algorithm
def HasSufficientBuyingPowerForOrder(self, parameters):
# custom behavior: this model will assume that there is always enough buying power
hasSufficientBuyingPowerForOrderResult = HasSufficientBuyingPowerForOrderResult(True)
self.algorithm.Log(f"CustomBuyingPowerModel: {hasSufficientBuyingPowerForOrderResult.IsSufficient}")
return hasSufficientBuyingPowerForOrderResult

View File

@@ -0,0 +1,97 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License
from datetime import datetime, timedelta
import clr
from System import *
from System.Reflection import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Orders import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Future import *
from QuantConnect import Market
### <summary>
### This regression algorithm tests In The Money (ITM) future option calls across different strike prices.
### We expect 6 orders from the algorithm, which are:
###
### * (1) Initial entry, buy ES Call Option (ES19M20 expiring ITM)
### * (2) Initial entry, sell ES Call Option at different strike (ES20H20 expiring ITM)
### * [2] Option assignment, opens a position in the underlying (ES20H20, Qty: -1)
### * [2] Future contract liquidation, due to impending expiry
### * [1] Option exercise, receive 1 ES19M20 future contract
### * [1] Liquidate ES19M20 contract, due to expiry
###
### Additionally, we test delistings for future options and assert that our
### portfolio holdings reflect the orders the algorithm has submitted.
### </summary>
class FutureOptionBuySellCallIntradayRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 5)
self.SetEndDate(2020, 6, 30)
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
# which causes delisting events to never be processed, thus leading to options that might never
# be exercised until the next data point arrives.
self.AddEquity("AAPL", Resolution.Daily)
self.es20h20 = self.AddFutureContract(
Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
datetime(2020, 3, 20)
),
Resolution.Minute).Symbol
self.es19m20 = self.AddFutureContract(
Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
datetime(2020, 6, 19)
),
Resolution.Minute).Symbol
# Select a future option expiring ITM, and adds it to the algorithm.
self.esOptions = [
self.AddFutureOptionContract(i, Resolution.Minute).Symbol for i in (self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) + self.OptionChainProvider.GetOptionContractList(self.es20h20, self.Time)) if i.ID.StrikePrice == 3200.0 and i.ID.OptionRight == OptionRight.Call
]
self.expectedContracts = [
Symbol.CreateOption(self.es20h20, Market.CME, OptionStyle.American, OptionRight.Call, 3200.0, datetime(2020, 3, 20)),
Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3200.0, datetime(2020, 6, 19))
]
for esOption in self.esOptions:
if esOption not in self.expectedContracts:
raise AssertionError(f"Contract {esOption} was not found in the chain")
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduleCallbackBuy)
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.Noon, self.ScheduleCallbackLiquidate)
def ScheduleCallbackBuy(self):
self.MarketOrder(self.esOptions[0], 1)
self.MarketOrder(self.esOptions[1], -1)
def ScheduleCallbackLiquidate(self):
self.Liquidate()
def OnEndOfAlgorithm(self):
if self.Portfolio.Invested:
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")

View File

@@ -0,0 +1,143 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License
from datetime import datetime, timedelta
import clr
from System import *
from System.Reflection import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Orders import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Future import *
from QuantConnect import Market
### <summary>
### This regression algorithm tests In The Money (ITM) future option expiry for calls.
### We expect 3 orders from the algorithm, which are:
###
### * Initial entry, buy ES Call Option (expiring ITM)
### * Option exercise, receiving ES future contracts
### * Future contract liquidation, due to impending expiry
###
### Additionally, we test delistings for future options and assert that our
### portfolio holdings reflect the orders the algorithm has submitted.
### </summary>
class FutureOptionCallITMExpiryRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 5)
self.SetEndDate(2020, 6, 30)
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
# which causes delisting events to never be processed, thus leading to options that might never
# be exercised until the next data point arrives.
self.AddEquity("AAPL", Resolution.Daily)
self.es19m20 = self.AddFutureContract(
Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
datetime(2020, 6, 19)
),
Resolution.Minute).Symbol
# Select a future option expiring ITM, and adds it to the algorithm.
self.esOption = self.AddFutureOptionContract(
list(
sorted([x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice <= 3200.0 and x.ID.OptionRight == OptionRight.Call], key=lambda x: x.ID.StrikePrice, reverse=True)
)[0], Resolution.Minute).Symbol
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3200.0, datetime(2020, 6, 19))
if self.esOption != self.expectedContract:
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain")
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduleCallback)
def ScheduleCallback(self):
self.MarketOrder(self.esOption, 1)
def OnData(self, data: Slice):
# Assert delistings, so that we can make sure that we receive the delisting warnings at
# the expected time. These assertions detect bug #4872
for delisting in data.Delistings.Values:
if delisting.Type == DelistingType.Warning:
if delisting.Time != datetime(2020, 6, 19):
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}")
elif delisting.Type == DelistingType.Delisted:
if delisting.Time != datetime(2020, 6, 20):
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}")
def OnOrderEvent(self, orderEvent: OrderEvent):
if orderEvent.Status != OrderStatus.Filled:
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
return
if not self.Securities.ContainsKey(orderEvent.Symbol):
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
security = self.Securities[orderEvent.Symbol]
if security.Symbol == self.es19m20:
self.AssertFutureOptionOrderExercise(orderEvent, security, self.Securities[self.expectedContract])
elif security.Symbol == self.expectedContract:
# Expected contract is ES19H21 Call Option expiring ITM @ 3250
self.AssertFutureOptionContractOrder(orderEvent, security)
else:
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
self.Log(f"{self.Time} -- {orderEvent.Symbol} :: Price: {self.Securities[orderEvent.Symbol].Holdings.Price} Qty: {self.Securities[orderEvent.Symbol].Holdings.Quantity} Direction: {orderEvent.Direction} Msg: {orderEvent.Message}")
def AssertFutureOptionOrderExercise(self, orderEvent: OrderEvent, future: Security, optionContract: Security):
# We expect the liquidation to occur on the day of the delisting (while the market is open),
# but currently we liquidate at the next market open (AAPL open) which happens to be
# at 9:30:00 Eastern Time. For unknown reasons, the delisting happens two minutes after the
# market open.
# Read more about the issue affecting this test here: https://github.com/QuantConnect/Lean/issues/4980
expectedLiquidationTimeUtc = datetime(2020, 6, 22, 13, 32, 0)
if orderEvent.Direction == OrderDirection.Sell and future.Holdings.Quantity != 0:
# We expect the contract to have been liquidated immediately
raise AssertionError(f"Did not liquidate existing holdings for Symbol {future.Symbol}")
if orderEvent.Direction == OrderDirection.Sell and orderEvent.UtcTime.replace(tzinfo=None) != expectedLiquidationTimeUtc:
raise AssertionError(f"Liquidated future contract, but not at the expected time. Expected: {expectedLiquidationTimeUtc} - found {orderEvent.UtcTime.replace(tzinfo=None)}");
# No way to detect option exercise orders or any other kind of special orders
# other than matching strings, for now.
if "Option Exercise" in orderEvent.Message:
if orderEvent.FillPrice != 3200.0:
raise AssertionError("Option did not exercise at expected strike price (3200)")
if future.Holdings.Quantity != 1:
# Here, we expect to have some holdings in the underlying, but not in the future option anymore.
raise AssertionError(f"Exercised option contract, but we have no holdings for Future {future.Symbol}")
if optionContract.Holdings.Quantity != 0:
raise AssertionError(f"Exercised option contract, but we have holdings for Option contract {optionContract.Symbol}")
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, option: Security):
if orderEvent.Direction == OrderDirection.Buy and option.Holdings.Quantity != 1:
raise AssertionError(f"No holdings were created for option contract {option.Symbol}")
if orderEvent.Direction == OrderDirection.Sell and option.Holdings.Quantity != 0:
raise AssertionError(f"Holdings were found after a filled option exercise")
if "Exercise" in orderEvent.Message and option.Holdings.Quantity != 0:
raise AssertionError(f"Holdings were found after exercising option contract {option.Symbol}")
def OnEndOfAlgorithm(self):
if self.Portfolio.Invested:
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")

View File

@@ -0,0 +1,127 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License
from datetime import datetime, timedelta
import clr
from System import *
from System.Reflection import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Orders import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Future import *
from QuantConnect import Market
### <summary>
### This regression algorithm tests Out of The Money (OTM) future option expiry for calls.
### We expect 1 order from the algorithm, which are:
###
### * Initial entry, buy ES Call Option (expiring OTM)
### - contract expires worthless, not exercised, so never opened a position in the underlying
###
### Additionally, we test delistings for future options and assert that our
### portfolio holdings reflect the orders the algorithm has submitted.
### </summary>
### <remarks>
### Total Trades in regression algorithm should be 1, but expiration is counted as a trade.
### See related issue: https://github.com/QuantConnect/Lean/issues/4854
### </remarks>
class FutureOptionCallOTMExpiryRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 5)
self.SetEndDate(2020, 6, 30)
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
# which causes delisting events to never be processed, thus leading to options that might never
# be exercised until the next data point arrives.
self.AddEquity("AAPL", Resolution.Daily)
self.es19m20 = self.AddFutureContract(
Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
datetime(2020, 6, 19)),
Resolution.Minute).Symbol
# Select a future option expiring ITM, and adds it to the algorithm.
self.esOption = self.AddFutureOptionContract(
list(
sorted(
[x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice >= 3300.0 and x.ID.OptionRight == OptionRight.Call],
key=lambda x: x.ID.StrikePrice
)
)[0], Resolution.Minute).Symbol
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3300.0, datetime(2020, 6, 19))
if self.esOption != self.expectedContract:
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain");
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduledMarketOrder)
def ScheduledMarketOrder(self):
self.MarketOrder(self.esOption, 1)
def OnData(self, data: Slice):
# Assert delistings, so that we can make sure that we receive the delisting warnings at
# the expected time. These assertions detect bug #4872
for delisting in data.Delistings.Values:
if delisting.Type == DelistingType.Warning:
if delisting.Time != datetime(2020, 6, 19):
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}");
if delisting.Type == DelistingType.Delisted:
if delisting.Time != datetime(2020, 6, 20):
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}");
def OnOrderEvent(self, orderEvent: OrderEvent):
if orderEvent.Status != OrderStatus.Filled:
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
return
if not self.Securities.ContainsKey(orderEvent.Symbol):
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
security = self.Securities[orderEvent.Symbol]
if security.Symbol == self.es19m20:
raise AssertionError("Invalid state: did not expect a position for the underlying to be opened, since this contract expires OTM")
# Expected contract is ES19M20 Call Option expiring OTM @ 3300
if (security.Symbol == self.expectedContract):
self.AssertFutureOptionContractOrder(orderEvent, security)
else:
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
self.Log(f"{orderEvent}");
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, option: Security):
if orderEvent.Direction == OrderDirection.Buy and option.Holdings.Quantity != 1:
raise AssertionError(f"No holdings were created for option contract {option.Symbol}");
if orderEvent.Direction == OrderDirection.Sell and option.Holdings.Quantity != 0:
raise AssertionError("Holdings were found after a filled option exercise");
if orderEvent.Direction == OrderDirection.Sell and "OTM" not in orderEvent.Message:
raise AssertionError("Contract did not expire OTM");
if "Exercise" in orderEvent.Message:
raise AssertionError("Exercised option, even though it expires OTM");
def OnEndOfAlgorithm(self):
if self.Portfolio.Invested:
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")

View File

@@ -0,0 +1,142 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License
from datetime import datetime, timedelta
import clr
from System import *
from System.Reflection import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Orders import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Future import *
from QuantConnect import Market
### <summary>
### This regression algorithm tests In The Money (ITM) future option expiry for puts.
### We expect 3 orders from the algorithm, which are:
###
### * Initial entry, buy ES Put Option (expiring ITM) (buy, qty 1)
### * Option exercise, receiving short ES future contracts (sell, qty -1)
### * Future contract liquidation, due to impending expiry (buy qty 1)
###
### Additionally, we test delistings for future options and assert that our
### portfolio holdings reflect the orders the algorithm has submitted.
### </summary>
class FutureOptionPutITMExpiryRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 5)
self.SetEndDate(2020, 6, 30)
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
# which causes delisting events to never be processed, thus leading to options that might never
# be exercised until the next data point arrives.
self.AddEquity("AAPL", Resolution.Daily)
self.es19m20 = self.AddFutureContract(
Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
datetime(2020, 6, 19)
),
Resolution.Minute).Symbol
# Select a future option expiring ITM, and adds it to the algorithm.
self.esOption = self.AddFutureOptionContract(
list(
sorted([x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice >= 3300.0 and x.ID.OptionRight == OptionRight.Put], key=lambda x: x.ID.StrikePrice)
)[0], Resolution.Minute).Symbol
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3300.0, datetime(2020, 6, 19))
if self.esOption != self.expectedContract:
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain")
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduleCallback)
def ScheduleCallback(self):
self.MarketOrder(self.esOption, 1)
def OnData(self, data: Slice):
# Assert delistings, so that we can make sure that we receive the delisting warnings at
# the expected time. These assertions detect bug #4872
for delisting in data.Delistings.Values:
if delisting.Type == DelistingType.Warning:
if delisting.Time != datetime(2020, 6, 19):
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}")
elif delisting.Type == DelistingType.Delisted:
if delisting.Time != datetime(2020, 6, 20):
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}")
def OnOrderEvent(self, orderEvent: OrderEvent):
if orderEvent.Status != OrderStatus.Filled:
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
return
if not self.Securities.ContainsKey(orderEvent.Symbol):
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
security = self.Securities[orderEvent.Symbol]
if security.Symbol == self.es19m20:
self.AssertFutureOptionOrderExercise(orderEvent, security, self.Securities[self.expectedContract])
elif security.Symbol == self.expectedContract:
# Expected contract is ES19M20 Call Option expiring ITM @ 3250
self.AssertFutureOptionContractOrder(orderEvent, security)
else:
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
self.Log(f"{self.Time} -- {orderEvent.Symbol} :: Price: {self.Securities[orderEvent.Symbol].Holdings.Price} Qty: {self.Securities[orderEvent.Symbol].Holdings.Quantity} Direction: {orderEvent.Direction} Msg: {orderEvent.Message}")
def AssertFutureOptionOrderExercise(self, orderEvent: OrderEvent, future: Security, optionContract: Security):
# We expect the liquidation to occur on the day of the delisting (while the market is open),
# but currently we liquidate at the next market open (AAPL open) which happens to be
# at 9:30:00 Eastern Time. For unknown reasons, the delisting happens two minutes after the
# market open.
# Read more about the issue affecting this test here: https://github.com/QuantConnect/Lean/issues/4980
expectedLiquidationTimeUtc = datetime(2020, 6, 22, 13, 32, 0)
if orderEvent.Direction == OrderDirection.Buy and future.Holdings.Quantity != 0:
# We expect the contract to have been liquidated immediately
raise AssertionError(f"Did not liquidate existing holdings for Symbol {future.Symbol}")
if orderEvent.Direction == OrderDirection.Buy and orderEvent.UtcTime.replace(tzinfo=None) != expectedLiquidationTimeUtc:
raise AssertionError(f"Liquidated future contract, but not at the expected time. Expected: {expectedLiquidationTimeUtc} - found {orderEvent.UtcTime.replace(tzinfo=None)}");
# No way to detect option exercise orders or any other kind of special orders
# other than matching strings, for now.
if "Option Exercise" in orderEvent.Message:
if orderEvent.FillPrice != 3300.0:
raise AssertionError("Option did not exercise at expected strike price (3300)")
if future.Holdings.Quantity != -1:
# Here, we expect to have some holdings in the underlying, but not in the future option anymore.
raise AssertionError(f"Exercised option contract, but we have no holdings for Future {future.Symbol}")
if optionContract.Holdings.Quantity != 0:
raise AssertionError(f"Exercised option contract, but we have holdings for Option contract {optionContract.Symbol}")
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, option: Security):
if orderEvent.Direction == OrderDirection.Buy and option.Holdings.Quantity != 1:
raise AssertionError(f"No holdings were created for option contract {option.Symbol}")
if orderEvent.Direction == OrderDirection.Sell and option.Holdings.Quantity != 0:
raise AssertionError(f"Holdings were found after a filled option exercise")
if "Exercise" in orderEvent.Message and option.Holdings.Quantity != 0:
raise AssertionError(f"Holdings were found after exercising option contract {option.Symbol}")
def OnEndOfAlgorithm(self):
if self.Portfolio.Invested:
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")

View File

@@ -0,0 +1,127 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License
from datetime import datetime, timedelta
import clr
from System import *
from System.Reflection import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Orders import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Future import *
from QuantConnect import Market
### <summary>
### This regression algorithm tests Out of The Money (OTM) future option expiry for puts.
### We expect 1 order from the algorithm, which are:
###
### * Initial entry, buy ES Put Option (expiring OTM)
### - contract expires worthless, not exercised, so never opened a position in the underlying
###
### Additionally, we test delistings for future options and assert that our
### portfolio holdings reflect the orders the algorithm has submitted.
### </summary>
### <remarks>
### Total Trades in regression algorithm should be 1, but expiration is counted as a trade.
### </remarks>
class FutureOptionPutOTMExpiryRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 5)
self.SetEndDate(2020, 6, 30)
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
# which causes delisting events to never be processed, thus leading to options that might never
# be exercised until the next data point arrives.
self.AddEquity("AAPL", Resolution.Daily)
self.es19m20 = self.AddFutureContract(
Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
datetime(2020, 6, 19)),
Resolution.Minute).Symbol
# Select a future option expiring ITM, and adds it to the algorithm.
self.esOption = self.AddFutureOptionContract(
list(
sorted(
[x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice <= 3150.0 and x.ID.OptionRight == OptionRight.Put],
key=lambda x: x.ID.StrikePrice,
reverse=True
)
)[0], Resolution.Minute).Symbol
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3150.0, datetime(2020, 6, 19))
if self.esOption != self.expectedContract:
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain");
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduledMarketOrder)
def ScheduledMarketOrder(self):
self.MarketOrder(self.esOption, 1)
def OnData(self, data: Slice):
# Assert delistings, so that we can make sure that we receive the delisting warnings at
# the expected time. These assertions detect bug #4872
for delisting in data.Delistings.Values:
if delisting.Type == DelistingType.Warning:
if delisting.Time != datetime(2020, 6, 19):
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}");
if delisting.Type == DelistingType.Delisted:
if delisting.Time != datetime(2020, 6, 20):
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}");
def OnOrderEvent(self, orderEvent: OrderEvent):
if orderEvent.Status != OrderStatus.Filled:
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
return
if not self.Securities.ContainsKey(orderEvent.Symbol):
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
security = self.Securities[orderEvent.Symbol]
if security.Symbol == self.es19m20:
raise AssertionError("Invalid state: did not expect a position for the underlying to be opened, since this contract expires OTM")
# Expected contract is ES19M20 Put Option expiring OTM @ 3200
if (security.Symbol == self.expectedContract):
self.AssertFutureOptionContractOrder(orderEvent, security)
else:
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
self.Log(f"{orderEvent}");
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, option: Security):
if orderEvent.Direction == OrderDirection.Buy and option.Holdings.Quantity != 1:
raise AssertionError(f"No holdings were created for option contract {option.Symbol}");
if orderEvent.Direction == OrderDirection.Sell and option.Holdings.Quantity != 0:
raise AssertionError("Holdings were found after a filled option exercise");
if orderEvent.Direction == OrderDirection.Sell and "OTM" not in orderEvent.Message:
raise AssertionError("Contract did not expire OTM");
if "Exercise" in orderEvent.Message:
raise AssertionError("Exercised option, even though it expires OTM");
def OnEndOfAlgorithm(self):
if self.Portfolio.Invested:
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")

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
from datetime import datetime, timedelta
import clr
from System import *
from System.Reflection import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Orders import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Future import *
from QuantConnect import Market
### <summary>
### This regression algorithm tests In The Money (ITM) future option expiry for short calls.
### We expect 3 orders from the algorithm, which are:
###
### * Initial entry, sell ES Call Option (expiring ITM)
### * Option assignment, sell 1 contract of the underlying (ES)
### * Future contract expiry, liquidation (buy 1 ES future)
###
### Additionally, we test delistings for future options and assert that our
### portfolio holdings reflect the orders the algorithm has submitted.
### </summary>
class FutureOptionShortCallITMExpiryRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 5)
self.SetEndDate(2020, 6, 30)
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
# which causes delisting events to never be processed, thus leading to options that might never
# be exercised until the next data point arrives.
self.AddEquity("AAPL", Resolution.Daily)
self.es19m20 = self.AddFutureContract(
Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
datetime(2020, 6, 19)),
Resolution.Minute).Symbol
# Select a future option expiring ITM, and adds it to the algorithm.
self.esOption = self.AddFutureOptionContract(
list(
sorted(
[x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice <= 3100.0 and x.ID.OptionRight == OptionRight.Call],
key=lambda x: x.ID.StrikePrice,
reverse=True
)
)[0], Resolution.Minute).Symbol
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3100.0, datetime(2020, 6, 19))
if self.esOption != self.expectedContract:
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain");
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduledMarketOrder)
def ScheduledMarketOrder(self):
self.MarketOrder(self.esOption, -1)
def OnData(self, data: Slice):
# Assert delistings, so that we can make sure that we receive the delisting warnings at
# the expected time. These assertions detect bug #4872
for delisting in data.Delistings.Values:
if delisting.Type == DelistingType.Warning:
if delisting.Time != datetime(2020, 6, 19):
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}");
if delisting.Type == DelistingType.Delisted:
if delisting.Time != datetime(2020, 6, 20):
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}");
def OnOrderEvent(self, orderEvent: OrderEvent):
if orderEvent.Status != OrderStatus.Filled:
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
return
if not self.Securities.ContainsKey(orderEvent.Symbol):
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
security = self.Securities[orderEvent.Symbol]
if security.Symbol == self.es19m20:
self.AssertFutureOptionOrderExercise(orderEvent, security, self.Securities[self.expectedContract])
elif security.Symbol == self.expectedContract:
self.AssertFutureOptionContractOrder(orderEvent, security)
else:
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
self.Log(f"{orderEvent}");
def AssertFutureOptionOrderExercise(self, orderEvent: OrderEvent, future: Security, optionContract: Security):
if "Assignment" in orderEvent.Message:
if orderEvent.FillPrice != 3100.0:
raise AssertionError("Option was not assigned at expected strike price (3100)")
if orderEvent.Direction != OrderDirection.Sell or future.Holdings.Quantity != -1:
raise AssertionError(f"Expected Qty: -1 futures holdings for assigned future {future.Symbol}, found {future.Holdings.Quantity}")
return
if orderEvent.Direction == OrderDirection.Buy and future.Holdings.Quantity != 0:
# We buy back the underlying at expiration, so we expect a neutral position then
raise AssertionError(f"Expected no holdings when liquidating future contract {future.Symbol}")
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, option: Security):
if orderEvent.Direction == OrderDirection.Sell and option.Holdings.Quantity != -1:
raise AssertionError(f"No holdings were created for option contract {option.Symbol}");
if orderEvent.IsAssignment and option.Holdings.Quantity != 0:
raise AssertionError(f"Holdings were found after option contract was assigned: {option.Symbol}")
def OnEndOfAlgorithm(self):
if self.Portfolio.Invested:
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")

View File

@@ -0,0 +1,119 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License
from datetime import datetime, timedelta
import clr
from System import *
from System.Reflection import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Orders import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Future import *
from QuantConnect import Market
### <summary>
### This regression algorithm tests Out of The Money (OTM) future option expiry for short calls.
### We expect 1 order from the algorithm, which are:
###
### * Initial entry, sell ES Call Option (expiring OTM)
### - Profit the option premium, since the option was not assigned.
###
### Additionally, we test delistings for future options and assert that our
### portfolio holdings reflect the orders the algorithm has submitted.
### </summary>
class FutureOptionShortCallOTMExpiryRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 5)
self.SetEndDate(2020, 6, 30)
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
# which causes delisting events to never be processed, thus leading to options that might never
# be exercised until the next data point arrives.
self.AddEquity("AAPL", Resolution.Daily)
self.es19m20 = self.AddFutureContract(
Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
datetime(2020, 6, 19)),
Resolution.Minute).Symbol
# Select a future option expiring ITM, and adds it to the algorithm.
self.esOption = self.AddFutureOptionContract(
list(
sorted(
[x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice >= 3400.0 and x.ID.OptionRight == OptionRight.Call],
key=lambda x: x.ID.StrikePrice
)
)[0], Resolution.Minute).Symbol
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3400.0, datetime(2020, 6, 19))
if self.esOption != self.expectedContract:
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain");
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduledMarketOrder)
def ScheduledMarketOrder(self):
self.MarketOrder(self.esOption, -1)
def OnData(self, data: Slice):
# Assert delistings, so that we can make sure that we receive the delisting warnings at
# the expected time. These assertions detect bug #4872
for delisting in data.Delistings.Values:
if delisting.Type == DelistingType.Warning:
if delisting.Time != datetime(2020, 6, 19):
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}");
if delisting.Type == DelistingType.Delisted:
if delisting.Time != datetime(2020, 6, 20):
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}");
def OnOrderEvent(self, orderEvent: OrderEvent):
if orderEvent.Status != OrderStatus.Filled:
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
return
if not self.Securities.ContainsKey(orderEvent.Symbol):
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
security = self.Securities[orderEvent.Symbol]
if security.Symbol == self.es19m20:
raise AssertionError(f"Expected no order events for underlying Symbol {security.Symbol}")
if security.Symbol == self.expectedContract:
self.AssertFutureOptionContractOrder(orderEvent, security)
else:
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
self.Log(f"{orderEvent}");
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, optionContract: Security):
if orderEvent.Direction == OrderDirection.Sell and optionContract.Holdings.Quantity != -1:
raise AssertionError(f"No holdings were created for option contract {optionContract.Symbol}")
if orderEvent.Direction == OrderDirection.Buy and optionContract.Holdings.Quantity != 0:
raise AssertionError("Expected no options holdings after closing position")
if orderEvent.IsAssignment:
raise AssertionError(f"Assignment was not expected for {orderEvent.Symbol}")
def OnEndOfAlgorithm(self):
if self.Portfolio.Invested:
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")

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
from datetime import datetime, timedelta
import clr
from System import *
from System.Reflection import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Orders import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Future import *
from QuantConnect import Market
### <summary>
### This regression algorithm tests In The Money (ITM) future option expiry for short puts.
### We expect 3 orders from the algorithm, which are:
###
### * Initial entry, sell ES Put Option (expiring ITM)
### * Option assignment, buy 1 contract of the underlying (ES)
### * Future contract expiry, liquidation (sell 1 ES future)
###
### Additionally, we test delistings for future options and assert that our
### portfolio holdings reflect the orders the algorithm has submitted.
### </summary>
class FutureOptionShortPutITMExpiryRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 5)
self.SetEndDate(2020, 6, 30)
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
# which causes delisting events to never be processed, thus leading to options that might never
# be exercised until the next data point arrives.
self.AddEquity("AAPL", Resolution.Daily)
self.es19m20 = self.AddFutureContract(
Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
datetime(2020, 6, 19)),
Resolution.Minute).Symbol
# Select a future option expiring ITM, and adds it to the algorithm.
self.esOption = self.AddFutureOptionContract(
list(
sorted(
[x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice <= 3400.0 and x.ID.OptionRight == OptionRight.Put],
key=lambda x: x.ID.StrikePrice,
reverse=True
)
)[0], Resolution.Minute).Symbol
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3400.0, datetime(2020, 6, 19))
if self.esOption != self.expectedContract:
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain");
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduledMarketOrder)
def ScheduledMarketOrder(self):
self.MarketOrder(self.esOption, -1)
def OnData(self, data: Slice):
# Assert delistings, so that we can make sure that we receive the delisting warnings at
# the expected time. These assertions detect bug #4872
for delisting in data.Delistings.Values:
if delisting.Type == DelistingType.Warning:
if delisting.Time != datetime(2020, 6, 19):
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}");
if delisting.Type == DelistingType.Delisted:
if delisting.Time != datetime(2020, 6, 20):
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}");
def OnOrderEvent(self, orderEvent: OrderEvent):
if orderEvent.Status != OrderStatus.Filled:
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
return
if not self.Securities.ContainsKey(orderEvent.Symbol):
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
security = self.Securities[orderEvent.Symbol]
if security.Symbol == self.es19m20:
self.AssertFutureOptionOrderExercise(orderEvent, security, self.Securities[self.expectedContract])
elif security.Symbol == self.expectedContract:
self.AssertFutureOptionContractOrder(orderEvent, security)
else:
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
self.Log(f"{orderEvent}");
def AssertFutureOptionOrderExercise(self, orderEvent: OrderEvent, future: Security, optionContract: Security):
if "Assignment" in orderEvent.Message:
if orderEvent.FillPrice != 3400.0:
raise AssertionError("Option was not assigned at expected strike price (3400)")
if orderEvent.Direction != OrderDirection.Buy or future.Holdings.Quantity != 1:
raise AssertionError(f"Expected Qty: 1 futures holdings for assigned future {future.Symbol}, found {future.Holdings.Quantity}")
return
if orderEvent.Direction == OrderDirection.Sell and future.Holdings.Quantity != 0:
# We buy back the underlying at expiration, so we expect a neutral position then
raise AssertionError(f"Expected no holdings when liquidating future contract {future.Symbol}")
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, option: Security):
if orderEvent.Direction == OrderDirection.Sell and option.Holdings.Quantity != -1:
raise AssertionError(f"No holdings were created for option contract {option.Symbol}");
if orderEvent.IsAssignment and option.Holdings.Quantity != 0:
raise AssertionError(f"Holdings were found after option contract was assigned: {option.Symbol}")
def OnEndOfAlgorithm(self):
if self.Portfolio.Invested:
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")

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
from datetime import datetime, timedelta
import clr
from System import *
from System.Reflection import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Orders import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Future import *
from QuantConnect import Market
### <summary>
### This regression algorithm tests Out of The Money (OTM) future option expiry for short puts.
### We expect 1 order from the algorithm, which are:
###
### * Initial entry, sell ES Put Option (expiring OTM)
### - Profit the option premium, since the option was not assigned.
###
### Additionally, we test delistings for future options and assert that our
### portfolio holdings reflect the orders the algorithm has submitted.
### </summary>
class FutureOptionShortPutOTMExpiryRegressionAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 5)
self.SetEndDate(2020, 6, 30)
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
# which causes delisting events to never be processed, thus leading to options that might never
# be exercised until the next data point arrives.
self.AddEquity("AAPL", Resolution.Daily)
self.es19m20 = self.AddFutureContract(
Symbol.CreateFuture(
Futures.Indices.SP500EMini,
Market.CME,
datetime(2020, 6, 19)),
Resolution.Minute).Symbol
# Select a future option expiring ITM, and adds it to the algorithm.
self.esOption = self.AddFutureOptionContract(
list(
sorted(
[x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice <= 3000.0 and x.ID.OptionRight == OptionRight.Put],
key=lambda x: x.ID.StrikePrice,
reverse=True
)
)[0], Resolution.Minute).Symbol
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3000.0, datetime(2020, 6, 19))
if self.esOption != self.expectedContract:
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain");
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduledMarketOrder)
def ScheduledMarketOrder(self):
self.MarketOrder(self.esOption, -1)
def OnData(self, data: Slice):
# Assert delistings, so that we can make sure that we receive the delisting warnings at
# the expected time. These assertions detect bug #4872
for delisting in data.Delistings.Values:
if delisting.Type == DelistingType.Warning:
if delisting.Time != datetime(2020, 6, 19):
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}");
if delisting.Type == DelistingType.Delisted:
if delisting.Time != datetime(2020, 6, 20):
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}");
def OnOrderEvent(self, orderEvent: OrderEvent):
if orderEvent.Status != OrderStatus.Filled:
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
return
if not self.Securities.ContainsKey(orderEvent.Symbol):
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
security = self.Securities[orderEvent.Symbol]
if security.Symbol == self.es19m20:
raise AssertionError(f"Expected no order events for underlying Symbol {security.Symbol}")
if security.Symbol == self.expectedContract:
self.AssertFutureOptionContractOrder(orderEvent, security)
else:
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
self.Log(f"{orderEvent}");
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, optionContract: Security):
if orderEvent.Direction == OrderDirection.Sell and optionContract.Holdings.Quantity != -1:
raise AssertionError(f"No holdings were created for option contract {optionContract.Symbol}")
if orderEvent.Direction == OrderDirection.Buy and optionContract.Holdings.Quantity != 0:
raise AssertionError("Expected no options holdings after closing position")
if orderEvent.IsAssignment:
raise AssertionError(f"Assignment was not expected for {orderEvent.Symbol}")
def OnEndOfAlgorithm(self):
if self.Portfolio.Invested:
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")

View File

@@ -12,15 +12,15 @@
# limitations under the License.
from clr import AddReference
AddReference("System.Core")
AddReference("QuantConnect.Common")
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Algorithm.Framework")
AddReference("QuantConnect.Common")
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import QCAlgorithm
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework.Selection import *
from QuantConnect.Data import *
from QuantConnect.Data.UniverseSelection import *
from datetime import timedelta

View File

@@ -14,7 +14,6 @@
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Algorithm.Framework")
AddReference("QuantConnect.Common")
from QuantConnect import *
@@ -24,7 +23,6 @@ from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Algorithm.Framework.Selection import *
from QuantConnect.Brokerages import *
from QuantConnect.Data import *
from QuantConnect.Interfaces import *
from QuantConnect.Orders import *
from System import *

View File

@@ -46,6 +46,10 @@
<ItemGroup>
<Content Include="AccumulativeInsightPortfolioRegressionAlgorithm.py" />
<Content Include="AddAlphaModelAlgorithm.py" />
<Content Include="AddFutureOptionContractDataStreamingRegressionAlgorithm.py" />
<Content Include="AddFutureOptionSingleOptionChainSelectedInUniverseFilterRegressionAlgorithm.py" />
<Content Include="AddOptionContractExpiresRegressionAlgorithm.py" />
<Content Include="AddOptionContractFromUniverseRegressionAlgorithm.py" />
<Content Include="AddRiskManagementAlgorithm.py" />
<Content Include="AddUniverseSelectionModelAlgorithm.py" />
<Content Include="Alphas\ContingentClaimsAnalysisDefaultPredictionAlpha.py" />
@@ -63,6 +67,7 @@
<Content Include="Alphas\VIXDualThrustAlpha.py" />
<Content Include="AltData\CachedAlternativeDataAlgorithm.py" />
<Content Include="AltData\BenzingaNewsAlgorithm.py" />
<Content Include="AltData\QuiverWallStreetBetsDataAlgorithm.py" />
<Content Include="AltData\SECReport8KAlgorithm.py" />
<Content Include="AltData\SmartInsiderTransactionAlgorithm.py" />
<Content Include="AltData\USTreasuryYieldCurveRateAlgorithm.py" />
@@ -83,16 +88,27 @@
<None Include="BasicTemplateOptionsPriceModel.py" />
<Content Include="Benchmarks\SECReportBenchmarkAlgorithm.py" />
<Content Include="Benchmarks\SmartInsiderEventBenchmarkAlgorithm.py" />
<Content Include="CoarseFineOptionUniverseChainRegressionAlgorithm.py" />
<Content Include="CoarseTiingoNewsUniverseSelectionAlgorithm.py" />
<Content Include="ConsolidateRegressionAlgorithm.py" />
<Content Include="CustomConsolidatorRegressionAlgorithm.py" />
<Content Include="CustomDataAddDataOnSecuritiesChangedRegressionAlgorithm.py" />
<Content Include="CustomDataAddDataCoarseSelectionRegressionAlgorithm.py" />
<None Include="CustomBuyingPowerModelAlgorithm.py" />
<Content Include="DynamicSecurityDataAlgorithm.py" />
<Content Include="ConfidenceWeightedFrameworkAlgorithm.py" />
<Content Include="ExtendedMarketTradingRegressionAlgorithm.py" />
<Content Include="FilterUniverseRegressionAlgorithm.py" />
<Content Include="FineFundamentalFilteredUniverseRegressionAlgorithm.py" />
<Content Include="FutureOptionBuySellCallIntradayRegressionAlgorithm.py" />
<Content Include="FutureOptionCallITMExpiryRegressionAlgorithm.py" />
<Content Include="FutureOptionCallOTMExpiryRegressionAlgorithm.py" />
<Content Include="FutureOptionPutITMExpiryRegressionAlgorithm.py" />
<Content Include="FutureOptionPutOTMExpiryRegressionAlgorithm.py" />
<Content Include="FutureOptionShortCallITMExpiryRegressionAlgorithm.py" />
<Content Include="FutureOptionShortCallOTMExpiryRegressionAlgorithm.py" />
<Content Include="FutureOptionShortPutITMExpiryRegressionAlgorithm.py" />
<Content Include="FutureOptionShortPutOTMExpiryRegressionAlgorithm.py" />
<Content Include="KerasNeuralNetworkAlgorithm.py" />
<Content Include="CustomDataUsingMapFileRegressionAlgorithm.py" />
<Content Include="LiquidETFUniverseFrameworkAlgorithm.py" />

View File

@@ -6,7 +6,8 @@
<ProjectHome>.</ProjectHome>
<StartupFile>
</StartupFile>
<SearchPath>stubs</SearchPath>
<SearchPath>
</SearchPath>
<WorkingDirectory>.</WorkingDirectory>
<OutputPath>.</OutputPath>
<Name>QuantConnect.Algorithm.PythonTools</Name>
@@ -45,6 +46,7 @@
<Compile Include="ConstituentsQC500GeneratorAlgorithm.py" />
<Compile Include="ConstituentsUniverseRegressionAlgorithm.py" />
<Compile Include="ConvertToFrameworkAlgorithm.py" />
<Compile Include="CustomBuyingPowerModelAlgorithm.py" />
<Compile Include="CustomDataAddDataCoarseSelectionRegressionAlgorithm.py" />
<Compile Include="CustomDataAddDataOnSecuritiesChangedRegressionAlgorithm.py" />
<Compile Include="CustomDataAddDataRegressionAlgorithm.py" />

View File

@@ -1,6 +1,24 @@
QuantConnect Python Algorithm Project:
=============
## Local Python Autocomplete
To enable autocomplete for your local Python IDE, install the `quantconnect-stubs` package from PyPI using the following command:
```
pip install quantconnect-stubs
```
To update your autocomplete to the latest version, you can run the following command:
```
pip install --upgrade quantconnect-stubs
```
Copy and paste the imports found [here](#python-autocomplete-imports) to the top of your project file to enable autocomplete.
In addition, you can use [Skylight](https://www.quantconnect.com/skylight) to automatically sync local changes to the cloud.
------
## Running LEAN Locally with Python
Before we enable python support, follow the [installation instructions](https://github.com/QuantConnect/Lean#installation-instructions) to get LEAN running C# algorithms in your machine.
### Install Python 3.6:
@@ -15,25 +33,25 @@ Before we enable python support, follow the [installation instructions](https://
- Value of the variable: python installation path.
4. Install [pandas=0.25.3](https://pandas.pydata.org/) and its [dependencies](https://pandas.pydata.org/pandas-docs/stable/install.html#dependencies).
5. Install [wrapt=1.11.2](https://pypi.org/project/wrapt/) module.
6. Reboot computer to ensure changes are propogated.
6. Install [pyarrow=1.0.1](https://arrow.apache.org/install/) module.
7. Reboot computer to ensure changes are propagated.
#### [macOS](https://github.com/QuantConnect/Lean#macos)
1. Use the macOS x86-64 package installer from [Anaconda](https://repo.anaconda.com/archive/Anaconda3-5.2.0-MacOSX-x86_64.pkg) and follow "[Installing on macOS](https://docs.anaconda.com/anaconda/install/mac-os)" instructions from Anaconda documentation page.
2. Install [pandas=0.25.3](https://pandas.pydata.org/) and its [dependencies](https://pandas.pydata.org/pandas-docs/stable/install.html#dependencies).
3. Install [wrapt=1.11.2](https://pypi.org/project/wrapt/) module.
4. Install [pyarrow=1.0.1](https://arrow.apache.org/install/) module.
*Note:* If you encounter the "System.DllNotFoundException: python3.6m" runtime error when running Python algorithms on macOS:
1. Find `libpython3.6m.dylib` in your Python installation folder. If you installed Python with Anaconda, it may be find at
```
/Users/{your_user_name}/anaconda3/lib/libpython3.6m.dylib
```
2. Open `Lean/Launcher/bin/Debug/Python.Runtime.dll.config`, add the following text and save:
```
<configuration>
<dllmap dll="python3.6m" target="{the path in step 1 including libpython3.6m.dylib}" os="!windows"/>
</configuration>
```
1. Find `libpython3.6m.dylib` in your Python installation folder. If you installed Python with Anaconda, it may be found at
```
/Users/{your_user_name}/anaconda3/lib/libpython3.6m.dylib
```
2. Open `Lean/Launcher/bin/Debug/Python.Runtime.dll.config`, add the following text under `<configuration> ... </configuration>` and save:
```
<dllmap dll="python3.6m" target="{the path in step 1 including libpython3.6m.dylib}" os="osx"/>
```
Note: Specify the install of v3.6.8 _exactly_, i.e. if with conda `conda install python=3.6.8` as this is a known compatible version and other versions may have issues as of this writing.
#### [Linux](https://github.com/QuantConnect/Lean#linux-debian-ubuntu)
@@ -48,25 +66,41 @@ conda update -y python conda pip
conda install -y cython=0.29.11
conda install -y pandas=0.25.3
conda install -y wrapt=1.11.2
pip install pyarrow==1.0.1
```
*Note:* There is a [known issue](https://github.com/pythonnet/pythonnet/issues/609) with python 3.6.5 that prevents pythonnet installation, please upgrade python to version 3.6.8:
```
conda install -y python=3.6.8
```
*Note 1:* There is a [known issue](https://github.com/pythonnet/pythonnet/issues/609) with python 3.6.5 that prevents pythonnet installation, please upgrade python to version 3.6.8:
```
conda install -y python=3.6.8
```
*Note 2:* If you encounter the "System.DllNotFoundException: python3.6m" runtime error when running Python algorithms on Linux:
1. Find `libpython3.6m.so` in your Python installation folder. If you installed Python with Miniconda, it may be found at
```
/home/{your_user_name}/miniconda3/envs/{qc_environment}/lib/libpython3.6m.so
```
Note that you can create a new virtual environment with all required dependencies by executing:
```
conda create -n qc_environment python=3.6.8 cython=0.29.11 pandas=0.25.3 wrapt=1.11.2
```
2. Open `Lean/Launcher/bin/Debug/Python.Runtime.dll.config`, add the following text under `<configuration> ... </configuration>` and save:
```
<dllmap dll="python3.6m" target="{the path in step 1 including libpython3.6m.so}" os="linux"/>
```
### Run python algorithm
1. Update the [config](https://github.com/QuantConnect/Lean/blob/master/Launcher/config.json) to run the python algorithm:
```json
"algorithm-type-name": "BasicTemplateAlgorithm",
"algorithm-language": "Python",
"algorithm-location": "../../../Algorithm.Python/BasicTemplateAlgorithm.py",
```
```json
"algorithm-type-name": "BasicTemplateAlgorithm",
"algorithm-language": "Python",
"algorithm-location": "../../../Algorithm.Python/BasicTemplateAlgorithm.py",
```
2. Rebuild LEAN.
3. Run LEAN. You should see the same result of the C# algorithm you tested earlier.
___
#### Python.Runtime.dll compilation
### Python.NET development - Python.Runtime.dll compilation
LEAN users do **not** need to compile `Python.Runtime.dll`. The information below is targeted to developers who wish to improve it.
Download [QuantConnect/pythonnet](https://github.com/QuantConnect/pythonnet/) github clone or downloading the zip. If downloading the zip - unzip to a local pathway.
@@ -87,3 +121,45 @@ msbuild pythonnet.sln /nologo /v:quiet /t:Clean;Rebuild /p:Platform=x64 /p:Pytho
```
msbuild pythonnet.sln /nologo /v:quiet /t:Clean;Rebuild /p:Platform=x64 /p:PythonInteropFile="interop36m.cs" /p:Configuration=ReleaseMono /p:DefineConstants="PYTHON36,PYTHON3,UCS4,MONO_LINUX,PYTHON_WITH_PYMALLOC"
```
# Python Autocomplete Imports
Copy and paste these imports to the top of your Python file to enable a development experience equal to the cloud (these imports are exactly the same as the ones used in the QuantConnect Terminal).
```python
from QuantConnect import *
from QuantConnect.Parameters import *
from QuantConnect.Benchmarks import *
from QuantConnect.Brokerages import *
from QuantConnect.Util import *
from QuantConnect.Interfaces import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Selection import *
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Risk import *
from QuantConnect.Indicators import *
from QuantConnect.Data import *
from QuantConnect.Data.Consolidators import *
from QuantConnect.Data.Custom import *
from QuantConnect.Data.Fundamental import *
from QuantConnect.Data.Market import *
from QuantConnect.Data.UniverseSelection import *
from QuantConnect.Notifications import *
from QuantConnect.Orders import *
from QuantConnect.Orders.Fees import *
from QuantConnect.Orders.Fills import *
from QuantConnect.Orders.Slippage import *
from QuantConnect.Scheduling import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Equity import *
from QuantConnect.Securities.Forex import *
from QuantConnect.Securities.Interfaces import *
from datetime import date, datetime, timedelta
from QuantConnect.Python import *
from QuantConnect.Storage import *
QCAlgorithmFramework = QCAlgorithm
QCAlgorithmFrameworkBridge = QCAlgorithm
```

View File

@@ -1,402 +0,0 @@
# encoding: utf-8
# module QuantConnect.API calls itself API
# from QuantConnect.Common, Version=2.4.0.0, Culture=neutral, PublicKeyToken=null
# by generator 1.145
# no doc
# imports
import datetime
import Newtonsoft.Json
import Newtonsoft.Json.Linq
import QuantConnect
import QuantConnect.API
import QuantConnect.Packets
import System
import typing
# no functions
# classes
class BaseLiveAlgorithmSettings(System.object):
"""
Base class for settings that must be configured per Brokerage to create new algorithms via the API.
BaseLiveAlgorithmSettings(user: str, password: str, environment: BrokerageEnvironment, account: str)
BaseLiveAlgorithmSettings(user: str, password: str)
BaseLiveAlgorithmSettings(environment: BrokerageEnvironment, account: str)
BaseLiveAlgorithmSettings(account: str)
"""
@typing.overload
def __init__(self, user: str, password: str, environment: QuantConnect.BrokerageEnvironment, account: str) -> QuantConnect.API.BaseLiveAlgorithmSettings:
pass
@typing.overload
def __init__(self, user: str, password: str) -> QuantConnect.API.BaseLiveAlgorithmSettings:
pass
@typing.overload
def __init__(self, environment: QuantConnect.BrokerageEnvironment, account: str) -> QuantConnect.API.BaseLiveAlgorithmSettings:
pass
@typing.overload
def __init__(self, account: str) -> QuantConnect.API.BaseLiveAlgorithmSettings:
pass
def __init__(self, *args) -> QuantConnect.API.BaseLiveAlgorithmSettings:
pass
Account: str
Environment: QuantConnect.BrokerageEnvironment
Id: str
Password: str
User: str
class CreatedNode(QuantConnect.Api.RestResponse):
"""
Rest api response wrapper for node/create, reads in the nodes information into a
node object
CreatedNode()
"""
Node: QuantConnect.API.Node
class DefaultLiveAlgorithmSettings(QuantConnect.API.BaseLiveAlgorithmSettings):
"""
Default live algorithm settings
DefaultLiveAlgorithmSettings(user: str, password: str, environment: BrokerageEnvironment, account: str)
"""
def __init__(self, user: str, password: str, environment: QuantConnect.BrokerageEnvironment, account: str) -> QuantConnect.API.DefaultLiveAlgorithmSettings:
pass
class Dividend(System.object):
"""
Dividend returned from the api
Dividend()
"""
Date: datetime.datetime
DividendPerShare: float
ReferencePrice: float
Symbol: QuantConnect.Symbol
SymbolID: str
class DividendList(QuantConnect.Api.RestResponse):
"""
Collection container for a list of dividend objects
DividendList()
"""
Dividends: typing.List[QuantConnect.API.Dividend]
class FXCMLiveAlgorithmSettings(QuantConnect.API.BaseLiveAlgorithmSettings):
"""
Algorithm setting for trading with FXCM
FXCMLiveAlgorithmSettings(user: str, password: str, environment: BrokerageEnvironment, account: str)
"""
def __init__(self, user: str, password: str, environment: QuantConnect.BrokerageEnvironment, account: str) -> QuantConnect.API.FXCMLiveAlgorithmSettings:
pass
class InteractiveBrokersLiveAlgorithmSettings(QuantConnect.API.BaseLiveAlgorithmSettings):
"""
Live algorithm settings for trading with Interactive Brokers
InteractiveBrokersLiveAlgorithmSettings(user: str, password: str, account: str)
"""
def __init__(self, user: str, password: str, account: str) -> QuantConnect.API.InteractiveBrokersLiveAlgorithmSettings:
pass
class LiveAlgorithm(QuantConnect.Api.RestResponse):
"""
Live algorithm instance result from the QuantConnect Rest API.
LiveAlgorithm()
"""
Brokerage: str
DeployId: str
Error: str
Launched: datetime.datetime
ProjectId: int
Status: QuantConnect.AlgorithmStatus
Stopped: typing.Optional[datetime.datetime]
Subscription: str
class LiveAlgorithmApiSettingsWrapper(System.object):
"""
Helper class to put BaseLiveAlgorithmSettings in proper format.
LiveAlgorithmApiSettingsWrapper(projectId: int, compileId: str, serverType: str, settings: BaseLiveAlgorithmSettings, version: str)
"""
def __init__(self, projectId: int, compileId: str, serverType: str, settings: QuantConnect.API.BaseLiveAlgorithmSettings, version: str) -> QuantConnect.API.LiveAlgorithmApiSettingsWrapper:
pass
Brokerage: QuantConnect.API.BaseLiveAlgorithmSettings
CompileId: str
ProjectId: int
ServerType: str
VersionId: str
class LiveAlgorithmResults(QuantConnect.Api.RestResponse):
"""
Details a live algorithm from the "live/read" Api endpoint
LiveAlgorithmResults()
"""
LiveResults: QuantConnect.API.LiveResultsData
class LiveAlgorithmResultsJsonConverter(Newtonsoft.Json.JsonConverter):
"""
Custom JsonConverter for LiveResults data for live algorithms
LiveAlgorithmResultsJsonConverter()
"""
def CanConvert(self, objectType: type) -> bool:
pass
@staticmethod
def CreateLiveResultsFromJObject(jObject: Newtonsoft.Json.Linq.JObject) -> QuantConnect.API.LiveAlgorithmResults:
pass
def ReadJson(self, reader: Newtonsoft.Json.JsonReader, objectType: type, existingValue: object, serializer: Newtonsoft.Json.JsonSerializer) -> object:
pass
def WriteJson(self, writer: Newtonsoft.Json.JsonWriter, value: object, serializer: Newtonsoft.Json.JsonSerializer) -> None:
pass
CanWrite: bool
class LiveList(QuantConnect.Api.RestResponse):
"""
List of the live algorithms running which match the requested status
LiveList()
"""
Algorithms: typing.List[QuantConnect.API.LiveAlgorithm]
class LiveLog(QuantConnect.Api.RestResponse):
"""
Logs from a live algorithm
LiveLog()
"""
Logs: typing.List[str]
class LiveResultsData(System.object):
"""
Holds information about the state and operation of the live running algorithm
LiveResultsData()
"""
Resolution: QuantConnect.Resolution
Results: QuantConnect.Packets.LiveResult
Version: int
class Node(System.object):
"""
Node class built for API endpoints nodes/read and nodes/create.
Converts JSON properties from API response into data members for the class.
Contains all relevant information on a Node to interact through API endpoints.
Node()
"""
Busy: bool
CpuCount: int
Description: str
Id: str
Name: str
Prices: QuantConnect.API.NodePrices
ProjectName: str
Ram: float
SKU: str
Speed: float
UsedBy: str
class NodeList(QuantConnect.Api.RestResponse):
"""
Rest api response wrapper for node/read, contains sets of node lists for each
target environment. List are composed of QuantConnect.API.Node objects.
NodeList()
"""
BacktestNodes: typing.List[QuantConnect.API.Node]
LiveNodes: typing.List[QuantConnect.API.Node]
ResearchNodes: typing.List[QuantConnect.API.Node]
class NodePrices(System.object):
"""
Class for deserializing node prices from node object
NodePrices()
"""
Monthly: int
Yearly: int
class NodeType(System.Enum, System.IConvertible, System.IFormattable, System.IComparable):
"""
NodeTypes enum for all possible options of target environments
Used in conjuction with SKU class as a NodeType is a required parameter for SKU
enum NodeType, values: Backtest (0), Live (2), Research (1)
"""
value__: int
Backtest: 'NodeType'
Live: 'NodeType'
Research: 'NodeType'
class OandaLiveAlgorithmSettings(QuantConnect.API.BaseLiveAlgorithmSettings):
"""
Live algorithm settings for trading with Oanda
OandaLiveAlgorithmSettings(accessToken: str, environment: BrokerageEnvironment, account: str)
"""
def __init__(self, accessToken: str, environment: QuantConnect.BrokerageEnvironment, account: str) -> QuantConnect.API.OandaLiveAlgorithmSettings:
pass
AccessToken: str
DateIssued: str
class Prices(System.object):
"""
Prices rest response wrapper
Prices()
"""
Price: float
Symbol: QuantConnect.Symbol
SymbolID: str
Updated: datetime.datetime
class PricesList(QuantConnect.Api.RestResponse):
"""
Collection container for a list of prices objects
PricesList()
"""
Prices: typing.List[QuantConnect.API.Prices]
class SKU(System.object):
"""
Class for generating a SKU for a node with a given configuration
Every SKU is made up of 3 variables:
- Target environment (L for live, B for Backtest, R for Research)
- CPU core count
- Dedicated RAM (GB)
SKU(cores: int, memory: int, target: NodeType)
"""
def ToString(self) -> str:
pass
def __init__(self, cores: int, memory: int, target: QuantConnect.API.NodeType) -> QuantConnect.API.SKU:
pass
Cores: int
Memory: int
Target: QuantConnect.API.NodeType
class Split(System.object):
"""
Split returned from the api
Split()
"""
Date: datetime.datetime
ReferencePrice: float
SplitFactor: float
Symbol: QuantConnect.Symbol
SymbolID: str
class SplitList(QuantConnect.Api.RestResponse):
"""
Collection container for a list of split objects
SplitList()
"""
Splits: typing.List[QuantConnect.API.Split]
class TradierLiveAlgorithmSettings(QuantConnect.API.BaseLiveAlgorithmSettings):
"""
Live algorithm settings for trading with Tradier
TradierLiveAlgorithmSettings(accessToken: str, dateIssued: str, refreshToken: str, account: str)
"""
def __init__(self, accessToken: str, dateIssued: str, refreshToken: str, account: str) -> QuantConnect.API.TradierLiveAlgorithmSettings:
pass
AccessToken: str
DateIssued: str
Lifetime: str
RefreshToken: str

View File

@@ -1,26 +0,0 @@
# encoding: utf-8
# module QuantConnect.Algorithm.Framework.Alphas.Analysis.Functions calls itself Functions
# from QuantConnect.Common, Version=2.4.0.0, Culture=neutral, PublicKeyToken=null
# by generator 1.145
# no doc
# imports
import datetime
import QuantConnect.Algorithm.Framework.Alphas
import QuantConnect.Algorithm.Framework.Alphas.Analysis
import typing
# no functions
# classes
class BinaryInsightScoreFunction(System.object, QuantConnect.Algorithm.Framework.Alphas.Analysis.IInsightScoreFunction):
"""
Defines a scoring function that always returns 1 or 0.
You're either right or you're wrong with this one :)
BinaryInsightScoreFunction()
"""
def Evaluate(self, context: QuantConnect.Algorithm.Framework.Alphas.Analysis.InsightAnalysisContext, scoreType: QuantConnect.Algorithm.Framework.Alphas.InsightScoreType) -> float:
pass

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