Hi staff,
I have been hitting my head against the wall for weeks now trying to figure this out and learn your platform! I am trying to write a simple bot that buys 0.7 delta call credit spreads. I modified the code to just buy one spread at a time, and it keeps giving me insufficient buying power errors. How can I fix this?
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Data.Custom.CBOE;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Securities.Option;
namespace QuantConnect.Algorithm.CSharp
{
public class VentralCalibratedProcessor : QCAlgorithm
{
private Symbol _equitySymbol;
private Symbol _optionSymbol;
private QuantConnect.Indicators.RelativeStrengthIndex _rsi;
public decimal MinDeltaLimit = 0.70M;
public decimal MaxTradeSize = 0.25M;
private OptionContract _longCall;
public int DaysBeforeExp;
public override void Initialize()
{
SetStartDate(2018, 1, 1); //Set Start Date
SetEndDate(2018, 3, 1); //Set End Date
SetCash(5000); //Set Strategy Cash
var equity = AddEquity("SPY", Resolution.Minute);
var option = AddOption("SPY", Resolution.Minute);
this.Symbol(option.Symbol);
_equitySymbol = equity.Symbol;
_optionSymbol = option.Symbol;
// 14 day RSI
_rsi = this.RSI(equity.Symbol, 14);
// Filter by 12 strikes and expiration 5 to 10 days
option.SetFilter(universe => universe.IncludeWeeklys().Strikes(-15, 15).Expiration(5, 10));
option.PriceModel = OptionPriceModels.CrankNicolsonFD();
UniverseSettings.Leverage = 4;
// Adding this to reproduce GH issue #2314
SetWarmup(TimeSpan.FromMinutes(1));
// use the underlying equity as the benchmark
SetBenchmark(_equitySymbol);
this.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
//Schedule.On(DateRules.EveryDay("SPY"), TimeRules.Every(TimeSpan.FromMinutes(5)), () =>{
//Debug("Specific Time: Fired at : " + Time);
//});
}
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// Slice object keyed by symbol containing the stock data
public override void OnData(Slice slice)
{
if(this.IsWarmingUp)
return;
if (IsMarketOpen(_equitySymbol) == false)
{
return;
}
if (!Portfolio.Invested)
{
OptionChain chain;
if (slice.OptionChains.TryGetValue(_optionSymbol, out chain))
{
// Find options between delta ranges closest to expiry date
var itmCallDebitSpread = from option in chain
where Math.Abs(option.Greeks.Delta) <= MinDeltaLimit &&
option.Right == OptionRight.Call
orderby option.Expiry ascending, option.Strike
select option;
//foreach(var option in itmCallDebitSpread.ToList()){
//Debug(string.Format("Strike: {0} Expiration: {1} DTE: {2}", option.Strike, option.Expiry, (option.Expiry - Time).Days));
//}
// Get the spread call options
var longCall = itmCallDebitSpread.ToList()[0];
var shortCall = itmCallDebitSpread.ToList()[1];
_longCall = longCall;
// Get debit for potential trade
var spreadDebit = (longCall.AskPrice - shortCall.AskPrice) * 100;
int numOfSpreadsToBuy = 0;
bool canAffordToBuy = true;
// Only allow trade size of percentage of portfolio
var availableCash = Math.Floor(Portfolio.Cash * MaxTradeSize);
while(canAffordToBuy){
numOfSpreadsToBuy++;
// If we can afford to sell, increment amount
if((availableCash - (numOfSpreadsToBuy * spreadDebit)) < 0){
numOfSpreadsToBuy--;
canAffordToBuy = false;
}
}
// Sell the spreads
if(_rsi < 100)
{
//Debug("Spread Debit: $" + Math.Abs(numOfSpreadsToBuy * spreadDebit));
//Debug("Short Call Delta: " + shortCall.Greeks.Delta.ToString());
//Debug("Long Call Delta: " + longCall.Greeks.Delta.ToString());
//Debug(string.Format("Strikes - Long: {0} / Short: {1}", longCall.Strike, shortCall.Strike));
//Debug(string.Format("Expiry Date ({0}): {1}", (longCall.Expiry - Time).Days, longCall.Expiry));
// Buy the spreads (We use a loop for margin reasons)
for(int i = 1; i<= numOfSpreadsToBuy; i++){
}
Buy(longCall.Symbol, 1);
Sell(shortCall.Symbol, 1);
}
}
}
else{
// Close positions based on stop-loss
//foreach(var security in Securities){
//if(security.Value.Holdings.UnrealizedProfitPercent < -0.30M){
//Debug("Stop Loss - Profit Percent (P/L): " + (security.Value.Holdings.UnrealizedProfitPercent * 100) + "%");
//Liquidate(_optionSymbol);
//}
//}
// Close positions before expiration to prevent assignment
if(_longCall != null){
if((_longCall.Expiry - Time).Days <= DaysBeforeExp){
//Liquidate(_optionSymbol);
//Debug("Closed options contracts early.");
}
}
}
}
/// <summary>
/// Order fill event handler. On an order fill update the resulting information is passed to this method.
/// </summary>
/// <param name="orderEvent">Order event details containing details of the evemts</param>
/// <remarks>This method can be called asynchronously and so should only be used by seasoned C# experts. Ensure you use proper locks on thread-unsafe objects</remarks>
public override void OnOrderEvent(OrderEvent orderEvent)
{
//Log(orderEvent.ToString());
var order = Transactions.GetOrderById(orderEvent.OrderId);
if (order.Type == OrderType.OptionExercise){
Log(string.Format("{0}: {1}: {2}", Time, order.Type, orderEvent));
Liquidate();
}
}
public override void OnAssignmentOrderEvent(OrderEvent orderEvent){
Log(orderEvent.ToString());
Liquidate();
}
}
}
Backtest Handled Error: Order Error: id: 3, Insufficient buying power to complete order (Value:-26100), Reason: Id: 3, Initial Margin: -13707, Free Margin: 0
Order Error: id: 6, Insufficient buying power to complete order (Value:26050), Reason: Id: 6, Initial Margin: 13707, Free Margin: 0
Shile Wen
Hi Kevin,
That simply means we don't have enough funds to purchase the securities. To address this, we can increase the starting cash by calling SetCash with a larger number. Alternatively we can also use a custom buying power model that always allows us to trade no matter the size of the order. I've shown both in the attached backtest.
Best,
Shile Wen
Kevin Gonzalez
Hi Shile,
Thank you for your response! Althought if you see my code, I am only buying one call spread at a time, and it is nested inside the "!Portfolio.Invested" if statement. Why is it that the test just stops mid way, and says I have insufficient funds even though I am only buying one spread at any given moment?
Kevin Gonzalez
Hi Shile, I think this may be a bug in your system. I believe what is happening here is that when the options are both ITM, and they both get exercised, QuantConnect says I have Insufficient Funds to buy 100 shares, but it doesn't take into account that I have sold a call at a higher strike that is ITM as well.... So even buying a simple Bull Call Spread in a small account won't work.
I have also tried this using the "OptionStrategies.BullCallSpread" way and it yields the same results... This should be looked into because I am unable to write an algo that either buys are sells spreads correctly.
Kevin Gonzalez
Disregard my previous messages, it's late and I missed the points you were trying to make. I got it to work! Thank you so so much for the help!
Kevin Gonzalez
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!