Key Concepts
Globals and Statics
API Access by Algorithm Instance
You can access the QCAlgorithm API by passing the self
this
object into a constructor of your target class.
The class constructor receives it as a variable that you can use to initialize your algorithm as you need.
The following algorithm demonstrates this process:
public class CustomPartialFillModelAlgorithm : QCAlgorithm { private Symbol _spy; private SecurityHolding _holdings; public override void Initialize() { SetStartDate(2019, 1, 1); SetEndDate(2019, 3, 1); var equity = AddEquity("SPY", Resolution.Hour); _spy = equity.Symbol; _holdings = equity.Holdings; // Set the fill model with the instance of the algorithm equity.SetFillModel(new CustomPartialFillModel(this)); } public override void OnData(Slice data) { var openOrders = Transactions.GetOpenOrders(_spy); if (openOrders.Count != 0) return; if (Time.Day > 10 && _holdings.Quantity <= 0) { MarketOrder(_spy, 105, true); } else if (Time.Day > 20 && _holdings.Quantity >= 0) { MarketOrder(_spy, -100, true); } } internal class CustomPartialFillModel : FillModel { private readonly QCAlgorithm _algorithm; private readonly Dictionary<int, decimal> _absoluteRemainingByOrderId = new(); public CustomPartialFillModel(QCAlgorithm algorithm) : base() { _algorithm = algorithm; } public override OrderEvent MarketFill(Security asset, MarketOrder order) { if (!_absoluteRemainingByOrderId.TryGetValue(order.Id, out var absoluteRemaining)) { absoluteRemaining = order.AbsoluteQuantity; } var fill = base.MarketFill(asset, order); fill.FillQuantity = Math.Sign(order.Quantity) * 10m; if (Math.Min(Math.Abs(fill.FillQuantity), absoluteRemaining) == absoluteRemaining) { fill.FillQuantity = Math.Sign(order.Quantity) * absoluteRemaining; fill.Status = OrderStatus.Filled; _absoluteRemainingByOrderId.Remove(order.Id); } else { fill.Status = OrderStatus.PartiallyFilled; _absoluteRemainingByOrderId[order.Id] = absoluteRemaining - Math.Abs(fill.FillQuantity); var price = fill.FillPrice; // Use the instance of the algorithm to log the information _algorithm.Debug($"{_algorithm.Time} - Partial Fill - Remaining {_absoluteRemainingByOrderId[order.Id]} Price - {price}"); } return fill; } } }
class CustomPartialFillModelAlgorithm(QCAlgorithm): def initialize(self): self.set_start_date(2019, 1, 1) self.set_end_date(2019, 3, 1) equity = self.add_equity("SPY", Resolution.HOUR) self._spy = equity.symbol self._holdings = equity.holdings # Set the fill model with the instance of the algorithm equity.set_fill_model(CustomPartialFillModel(self)) def on_data(self, data): open_orders = self.transactions.get_open_orders(self.spy) if len(open_orders) != 0: return if self.time.day > 10 and self._holdings.quantity <= 0: self.market_order(self._spy, 105, True) elif self.time.day > 20 and self._holdings.quantity >= 0: self.market_order(self._spy, -100, True) class CustomPartialFillModel(FillModel): def __init__(self, algorithm): self._algorithm = algorithm self._absolute_remaining_by_order_id = {} def market_fill(self, asset, order): absolute_remaining = self._absolute_remaining_by_order_id.get(order.id, order.absolute_quantity) fill = super().market_fill(asset, order) fill.fill_quantity = np.sign(order.quantity) * 10 if (min(abs(fill.fill_quantity), absolute_remaining) == absolute_remaining): fill.fill_quantity = np.sign(order.quantity) * absolute_remaining fill.status = OrderStatus.FILLED self._absolute_remaining_by_order_id.pop(order.id, None) else: fill.status = OrderStatus.PARTIALLY_FILLED self._absolute_remaining_by_order_id[order.id] = absolute_remaining - abs(fill.fill_quantity) price = fill.fill_price # Use the instance of the algorithm to log the information self._algorithm.debug(f"{self._algorithm.time} - Partial Fill - Remaining {self._absolute_remaining_by_order_id[order.id]} Price - {price}") return fill
API Access by Global Static Variable
Occasionally, passing the QCAlgorithm object to your class constructor is impossible, so you need to use global static variables to access the API for debugging and initialization purposes. The most common case is custom data implementations, where the LEAN Engine is creating the objects and you're unable to specify a custom constructor.
To create a global static variable, assign the reference to the global variable in the algorithm's Initialize
initialize
method.
This assignment sets the active instance of the variable to the global static.
Then, in your custom class, you can access the QCAlgorithm API through the global.
The following algorithm demonstrates this process:
public class MyCustomDataTypeAlgorithm : QCAlgorithm { public override void Initialize() { MyCustomDataType.ALGORITHM = this; var symbol = AddData<MyCustomDataType>("<name>", Resolution.Daily).Symbol; } } public class MyCustomDataType : BaseData { public static QCAlgorithm ALGORITHM; public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode) { if (!char.IsDigit(line.Trim()[0])) { // Display the line with the header ALGORITHM.Debug($"HEADER: {line}"); return null; } var data = line.Split(','); return new MyCustomDataType() { Time = DateTime.ParseExact(data[0], "yyyyMMdd", CultureInfo.InvariantCulture), EndTime = Time.AddDays(1), Symbol = config.Symbol, Value = data[1].IfNotNullOrEmpty( s => decimal.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture)), }; } }
class MyAlgorithmInstance(QCAlgorithm): def initialize(self): self.set_cash(100000) self.set_start_date(1998, 1, 1) self.set_end_date(2014, 6, 1) # Assign self to Static Cape Variable. Cape.algorithm = self self.add_data(Cape, "CAPE") def on_data(self, data): self.plot("CAPE", "Value", data["CAPE"].value) class Cape(PythonData): def get_source(self, config, date, isLiveMode): Cape.algorithm.debug("Test Static: GetSource") return SubscriptionDataSource("https://www.dropbox.com/scl/fi/mqbnfb7ll88nne7b8ymy7/CAPE.csv?rlkey=mnu0ax1d8lcj3gzkdw79z0pm8&dl=1", SubscriptionTransportMedium.REMOTE_FILE) def reader(self, config, line, date, isLiveMode): if not (line.strip() and line[0].isdigit()): return None index = Cape() try: data = line.split(',') index.symbol = config.symbol index.time = datetime.strptime(data[0], "%Y-%m") index.value = float(data[10]) except ValueError: Cape.algorithm.debug("Test Static 2: ValueError") return None return index