| Overall Statistics |
|
Total Trades 12 Average Win 0.00% Average Loss 0.00% Compounding Annual Return -0.241% Drawdown 0.000% Expectancy -0.593 Net Profit -0.002% Sharpe Ratio -10.938 Loss Rate 67% Win Rate 33% Profit-Loss Ratio 0.22 Alpha -0.001 Beta -0.104 Annual Standard Deviation 0 Annual Variance 0 Information Ratio -18.797 Tracking Error 0.001 Treynor Ratio 0.014 Total Fees $12.00 |
import datetime
from QuantConnect import Orders
class Algorithm(QCAlgorithm):
def Initialize(self):
"""
In this example, we choose three companies,
and place two limit orders for each, every day.
At the end of the day, we cancel the limit orders
that have *not* executed, sell off the shares that
resulted from the limit orders that *have* executed,
and verify that both attempts have resulted in an
empty portfolio by the end of the day.
We place two limit orders for each company, with
limits of -1% and -10% relative to the company's
price at market open. One of the limit orders will
execute if and only if the company's stock price
drops one percentage point throughout the day,
relative to the open price, (a fairly common
occurrence), while the other of the two limit
orders will execute if and only if the company's
stock price drops ten percent throughout the day
(a very uncommon occurrence).
These parameters were chosen for illustrative
purposes, so that the typical trading day will
come to an end the with us having two tasks we
need to accomplish: we will have one sucessfully
executed limit order (for which we need to sell off
the shares we bought during the day), and we will
have another limit order which we placed, but which
generally will not have filled (for which we need
to cancel the pending limit order).
The resulting log demonstrates that we can successfully
close out types of positions at the day's end,
and then verify that we have.
"""
self.SetStartDate(2018, 6, 25)
self.SetEndDate(2018, 6, 27)
self.SetCash(1_000_000)
tickers = ["AAPL", "GOOGL", "MSFT"]
for ticker in tickers:
self.AddEquity(ticker, Resolution.Minute)
self.Schedule.On(
self.DateRules.EveryDay(),
self.TimeRules.At(9, 31),
self.marketOpenCallback
)
self.Schedule.On(
self.DateRules.EveryDay(),
self.TimeRules.At(15, 59),
self.marketCloseCallback
)
self.Schedule.On(
self.DateRules.EveryDay(),
self.TimeRules.At(23, 0),
self.nightTimeCallback
)
def OnData(self, data):
"""
This method is normally called for each timestep
of the data we have subscribed to. For example, if
we called self.AddEquity('AAPL', Resolution.Minute)
in the Initialize method, then the OnData method will
be called every minute, and similarly for any other
resolution we might have subscribed to. In this
example, we will not have any reason to call this
method, but the framework requires us to include it.
"""
pass
def marketOpenCallback(self):
"""
Scheduled to be called soon after market open.
In this function we place two limit orders for
each ticker that we added to our equities in the
Initialize method.
"""
self.Debug(f"Executing marketOpenCallback")
for ticker in self.Securities.Keys:
price = self.getPrice(ticker)
fraction_small = 0.01
fraction_large = 0.10
self.Debug(f"Placing two limit orders for {ticker}")
self.LimitOrder(ticker, 1, price * (1 - fraction_small))
self.LimitOrder(ticker, 1, price * (1 - fraction_large))
def marketCloseCallback(self):
"""
Scheduled to be called soon before market close.
In this function we attempt to:
(1) Sell off any remaining holdings.
(2) Cancel any open limit orders.
However, the resulting events can execute
asynchronously in principle, so we have to
wait until some time *after* the market is
closed in order to check whether or not we
have successfully performed (1) and (2).
"""
self.Debug(f"Executing marketCloseCallback")
for ticker in self.Securities.Keys:
openOrders = self.Transactions.GetOpenOrders(ticker)
invested = self.Portfolio[ticker].Invested
quantity = self.Portfolio[ticker].Quantity
numOpen = len(openOrders)
# Cancel open limit orders that never hit their thresholds.
if numOpen > 0:
self.Debug(f"Cancelling {numOpen} open orders for {ticker}")
self.Transactions.CancelOpenOrders(ticker)
else:
self.Debug(f"No remaining open order for {ticker}.")
# Immediately sell off any remaining shares we're still holding.
if invested:
self.Debug(f"Selling off our {quantity} shares of {ticker}")
self.MarketOrder(ticker, -quantity)
else:
self.Debug(f"No longer invested in {ticker}.")
assert invested == (quantity > 0), (
"Error: Quantity and invested don't agree."
)
def nightTimeCallback(self):
"""
Scheduled to be called after market close.
In this function we ensure that we properly:
(1) Sold off any remaining holdings.
(2) Cancelled any open limit orders.
"""
self.Debug(f"Executing nightTimeCallback")
anyInvested = self.Portfolio.Invested
openOrders = self.Transactions.GetOpenOrders()
numOpen = len(openOrders)
self.Debug(f"Any investments remaining?: {anyInvested}")
self.Debug(f"Number of open orders: {numOpen}")
assert not anyInvested, (
"Error: Not all investments were sold off before close."
)
assert len(openOrders) == 0, (
"Error: Not all open limit orders were cancelled before close."
)
def getPrice(self, symbol):
return float(self.Securities[symbol].Price)
def formatEventType(self, orderEvent):
"""
This method turns the numeric values we receive from
orderEvent.Status into a human readable string so we
can tell what kind of event is being passed when the
OnOrderEvent method is automatically called on fills
"""
statuses = ('New', 'Submitted', 'PartiallyFilled', 'Filled',
'Canceled', 'None', 'Invalid', 'CancelPending')
order_statuses = {
getattr(Orders.OrderStatus, status): status
for status in statuses
}
return order_statuses[orderEvent.Status]
def OnOrderEvent(self, orderEvent):
"""
This method is called by the QuantConnect framework whenever
an "order event" occurs. Common events occur at the moment
when an order is submitted, fully filled, partially filled,
cancelled, or at the moment cancellation is requested, but
before the cancellation occurs. For our purposes, the most
relevant event type is the one which is emitted when a
limit order fills.
"""
symbol = orderEvent.Symbol
filled = orderEvent.FillQuantity
type = self.formatEventType(orderEvent)
self.Debug(f"Got '{type}' orderEvent for {symbol}, "
f"fill quantity is {filled}")