708 lines
33 KiB
C#
708 lines
33 KiB
C#
/*
|
|
* 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 NAMESPACES
|
|
**********************************************************/
|
|
using System;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Threading;
|
|
using System.ComponentModel.Composition;
|
|
using System.ComponentModel.Composition.Hosting;
|
|
using QuantConnect.Brokerages;
|
|
using QuantConnect.Interfaces;
|
|
using QuantConnect.Lean.Engine.DataFeeds;
|
|
using QuantConnect.Lean.Engine.RealTime;
|
|
using QuantConnect.Lean.Engine.Results;
|
|
using QuantConnect.Lean.Engine.Setup;
|
|
using QuantConnect.Lean.Engine.TransactionHandlers;
|
|
using QuantConnect.Logging;
|
|
using QuantConnect.Orders;
|
|
using QuantConnect.Packets;
|
|
using QuantConnect.Configuration;
|
|
|
|
namespace QuantConnect.Lean.Engine
|
|
{
|
|
/********************************************************
|
|
* CLASS DEFINITIONS
|
|
*********************************************************/
|
|
/// <summary>
|
|
/// LEAN ALGORITHMIC TRADING ENGINE: ENTRY POINT.
|
|
///
|
|
/// The engine loads new tasks, create the algorithms and threads, and sends them
|
|
/// to Algorithm Manager to be executed. It is the primary operating loop.
|
|
/// </summary>
|
|
public class Engine
|
|
{
|
|
/********************************************************
|
|
* CLASS PRIVATE VARIABLES
|
|
*********************************************************/
|
|
private static bool _liveMode = Config.GetBool("livemode");
|
|
private static bool _local = Config.GetBool("local");
|
|
private static IBrokerage _brokerage = new Brokerage();
|
|
|
|
/********************************************************
|
|
* CLASS PUBLIC VARIABLES
|
|
*********************************************************/
|
|
/// <summary>
|
|
/// Datafeed handler creates local, live, historical data feed management all through specific dedicated DLL's.
|
|
/// </summary>
|
|
public static IDataFeed DataFeed;
|
|
|
|
/// <summary>
|
|
/// Result handler pushes result messages to live API, backtesting API or console for local.
|
|
/// </summary>
|
|
public static IResultHandler ResultHandler;
|
|
|
|
/// <summary>
|
|
/// Transaction handler pushes trades to historical models, or live market with brokerage.
|
|
/// </summary>
|
|
public static ITransactionHandler TransactionHandler;
|
|
|
|
/// <summary>
|
|
/// Setup handler initializes all backtest requirements and sets up the algorithms internal state.
|
|
/// </summary>
|
|
public static ISetupHandler SetupHandler;
|
|
|
|
/// <summary>
|
|
/// RealTime events handlers trigger function callbacks at specific times during the day for the algorithms.
|
|
/// Works for backtests and live trading.
|
|
/// </summary>
|
|
public static IRealTimeHandler RealTimeHandler;
|
|
|
|
/// <summary>
|
|
/// Brokerage class holds manages the connection, transaction processing and data retrieval from specific broker endpoints.
|
|
/// </summary>
|
|
public static IBrokerage Brokerage
|
|
{
|
|
get
|
|
{
|
|
return _brokerage;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Notification/messaging handler for pushing messages to the proper endpoint.
|
|
/// </summary>
|
|
public static IMessagingHandler Notify;
|
|
|
|
/// <summary>
|
|
/// Task requester / job queue handler for running the next algorithm task.
|
|
/// </summary>
|
|
public static ITaskHandler Tasks;
|
|
|
|
/// <summary>
|
|
/// Algorithm controls handler for setting the per user restrictions on algorithm behaviour where applicable.
|
|
/// </summary>
|
|
public static IControls Controls;
|
|
|
|
|
|
/********************************************************
|
|
* CLASS PROPERTIES
|
|
*********************************************************/
|
|
/// <summary>
|
|
/// Are we operating this as a local independent node, independent of the cloud.
|
|
/// Running on a local algorithm, and local datasources.
|
|
/// </summary>
|
|
public static bool IsLocal
|
|
{
|
|
get
|
|
{
|
|
return _local;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Instance is a micro tagged qc.live.v3 for live use only:
|
|
/// -> Monitor the live job queue, not the backtest queue:
|
|
/// </summary>
|
|
public static bool LiveMode
|
|
{
|
|
get
|
|
{
|
|
return _liveMode;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Maximum allowable ram for an algorithm
|
|
/// </summary>
|
|
public static int MaximumRamAllocation
|
|
{
|
|
get
|
|
{
|
|
//Total Physical Ram Available:
|
|
var allocation = 1024;
|
|
var ram = Convert.ToInt32(OS.TotalPhysicalMemory);
|
|
if (ram < allocation)
|
|
{
|
|
allocation = ram - 200;
|
|
}
|
|
|
|
Log.Trace("Engine.MaximumRamAllocation(): Allocated: " + allocation);
|
|
|
|
return allocation;
|
|
}
|
|
}
|
|
|
|
/********************************************************
|
|
* CLASS METHODS
|
|
*********************************************************/
|
|
/// <summary>
|
|
/// Primary Analysis Thread:
|
|
/// </summary>
|
|
public static void Main(string[] args)
|
|
{
|
|
//Initialize:
|
|
AlgorithmNodePacket job = null;
|
|
var timer = Stopwatch.StartNew();
|
|
var algorithm = default(IAlgorithm);
|
|
|
|
//Name thread for the profiler:
|
|
Thread.CurrentThread.Name = "Algorithm Analysis Thread";
|
|
Log.Trace("Engine.Main(): Started " + DateTime.Now.ToShortTimeString());
|
|
Log.Trace("Engine.Main(): Memory " + OS.ApplicationMemoryUsed + "Mb-App " + +OS.TotalPhysicalMemoryUsed + "Mb-Used " + OS.TotalPhysicalMemory + "Mb-Total");
|
|
|
|
//Import external libraries specific to physical server location (cloud/local)
|
|
var catalog = new AggregateCatalog();
|
|
catalog.Catalogs.Add(new DirectoryCatalog(@"../../Extensions"));
|
|
var container = new CompositionContainer(catalog);
|
|
try
|
|
{
|
|
// grab the right export based on configuration
|
|
Notify = container.GetExportedValueByTypeName<IMessagingHandler>(Config.Get("messaging-handler"));
|
|
Tasks = container.GetExportedValueByTypeName<ITaskHandler>(Config.Get("task-handler"));
|
|
Controls = container.GetExportedValueByTypeName<IControls>(Config.Get("controls-handler"));
|
|
}
|
|
catch (CompositionException compositionException)
|
|
{ Log.Error("Engine.Main(): Failed to load library: " + compositionException);
|
|
}
|
|
|
|
//Setup packeting, queue and controls system: These don't do much locally.
|
|
Controls.Initialize();
|
|
Notify.Initialize();
|
|
Tasks.Initialize(_liveMode);
|
|
|
|
//Start monitoring the backtest active status:
|
|
var statusPingThread = new Thread(StateCheck.Ping.Run);
|
|
statusPingThread.Start();
|
|
|
|
do
|
|
{
|
|
try
|
|
{
|
|
//Clean up cache directories:
|
|
CleanUpDirectories();
|
|
|
|
//Reset algo manager internal variables preparing for a new algorithm.
|
|
AlgorithmManager.ResetManager();
|
|
|
|
//Reset thread holders.
|
|
var initializeComplete = false;
|
|
Thread threadFeed = null;
|
|
Thread threadTransactions = null;
|
|
Thread threadResults = null;
|
|
Thread threadRealTime = null;
|
|
|
|
//-> Pull job from QuantConnect job queue, or, pull local build:
|
|
var algorithmPath = "";
|
|
job = Tasks.NextTask(out algorithmPath); // Blocking.
|
|
|
|
//-> Initialize messaging system
|
|
Notify.SetChannel(job.Channel);
|
|
|
|
//-> Reset the backtest stopwatch; we're now running the algorithm.
|
|
timer.Restart();
|
|
|
|
//-> Create SetupHandler to configure internal algorithm state:
|
|
SetupHandler = GetSetupHandler(job.SetupEndpoint);
|
|
|
|
//-> Set the result handler type for this algorithm job, and launch the associated result thread.
|
|
ResultHandler = GetResultHandler(job);
|
|
threadResults = new Thread(ResultHandler.Run, 0) {Name = "Result Thread"};
|
|
threadResults.Start();
|
|
|
|
try
|
|
{
|
|
// Save algorithm to cache, load algorithm instance:
|
|
algorithm = SetupHandler.CreateAlgorithmInstance(algorithmPath);
|
|
|
|
//Initialize the internal state of algorithm and job: executes the algorithm.Initialize() method.
|
|
initializeComplete = SetupHandler.Setup(algorithm, out _brokerage, job);
|
|
|
|
//If there are any reasons it failed, pass these back to the IDE.
|
|
if (!initializeComplete || algorithm.ErrorMessages.Count > 0 || SetupHandler.Errors.Count > 0)
|
|
{
|
|
initializeComplete = false;
|
|
//Get all the error messages: internal in algorithm and external in setup handler.
|
|
var errorMessage = String.Join(",", algorithm.ErrorMessages);
|
|
errorMessage += String.Join(",", SetupHandler.Errors);
|
|
throw new Exception(errorMessage);
|
|
}
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
ResultHandler.RuntimeError("Algorithm.Initialize() Error: " + err.Message, err.StackTrace);
|
|
}
|
|
|
|
//-> Using the job + initialization: load the designated handlers:
|
|
if (initializeComplete)
|
|
{
|
|
//Set algorithm as locked; set it to live mode if we're trading live, and set it to locked for no further updates.
|
|
algorithm.SetAlgorithmId(job.AlgorithmId);
|
|
algorithm.SetLiveMode(LiveMode);
|
|
algorithm.SetLocked();
|
|
|
|
//Load the associated handlers for data, transaction and realtime events:
|
|
ResultHandler.SetAlgorithm(algorithm);
|
|
DataFeed = GetDataFeedHandler(algorithm, _brokerage, job);
|
|
TransactionHandler = GetTransactionHandler(algorithm, _brokerage, ResultHandler, job);
|
|
RealTimeHandler = GetRealTimeHandler(algorithm, _brokerage, DataFeed, ResultHandler, job);
|
|
|
|
//Set the error handlers for the brokerage asynchronous errors.
|
|
SetupHandler.SetupErrorHandler(ResultHandler, _brokerage);
|
|
|
|
//Send status to user the algorithm is now executing.
|
|
ResultHandler.SendStatusUpdate(job.AlgorithmId, AlgorithmStatus.Running);
|
|
|
|
//Launch the data, transaction and realtime handlers into dedicated threads
|
|
threadFeed = new Thread(DataFeed.Run, 0) {Name = "DataFeed Thread"};
|
|
threadTransactions = new Thread(TransactionHandler.Run, 0) { Name = "Transaction Thread" };
|
|
threadRealTime = new Thread(RealTimeHandler.Run, 0) {Name = "RealTime Thread"};
|
|
|
|
//Launch the data feed, result sending, and transaction models/handlers in separate threads.
|
|
threadFeed.Start(); // Data feed pushing data packets into thread bridge;
|
|
threadTransactions.Start(); // Transaction modeller scanning new order requests
|
|
threadRealTime.Start(); // RealTime scan time for time based events:
|
|
// Result manager scanning message queue: (started earlier)
|
|
|
|
try
|
|
{
|
|
// Execute the Algorithm Code:
|
|
var complete = Isolator.ExecuteWithTimeLimit(SetupHandler.MaximumRuntime, () =>
|
|
{
|
|
try
|
|
{
|
|
//Run Algorithm Job:
|
|
// -> Using this Data Feed,
|
|
// -> Send Orders to this TransactionHandler,
|
|
// -> Send Results to ResultHandler.
|
|
AlgorithmManager.Run(job, algorithm, DataFeed, TransactionHandler, ResultHandler, SetupHandler, RealTimeHandler);
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
//Debugging at this level is difficult, stack trace needed.
|
|
Log.Error("Engine.Run(): Error in Algo Manager: " + err.Message + " ST >> " + err.StackTrace);
|
|
}
|
|
|
|
Log.Trace("Engine.Run(): Exiting Algorithm Manager");
|
|
|
|
}, MaximumRamAllocation);
|
|
|
|
if (!complete)
|
|
{
|
|
Log.Error("Engine.Main(): Failed to complete in time: " + SetupHandler.MaximumRuntime.ToString("F"));
|
|
throw new Exception("Failed to complete algorithm within " + SetupHandler.MaximumRuntime.ToString("F") + " seconds. Please make it run faster.");
|
|
}
|
|
|
|
// Algorithm runtime error:
|
|
if (AlgorithmManager.RunTimeError != null)
|
|
{
|
|
throw AlgorithmManager.RunTimeError;
|
|
}
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
//Log:
|
|
Log.Error("Engine.Run(): Breaking out of parent try-catch: " + err.Message + " " + err.StackTrace);
|
|
//Error running the user algorithm: purge datafeed.
|
|
if (DataFeed != null) DataFeed.Exit();
|
|
//Send the error message:
|
|
if (ResultHandler != null) ResultHandler.RuntimeError("Runtime Error: " + err.Message, err.StackTrace);
|
|
}
|
|
|
|
//Send result data back: this entire code block could be rewritten.
|
|
// todo: - Split up statistics class, its enormous.
|
|
// todo: - Make a dedicated Statistics.Benchmark class.
|
|
// todo: - Elegently manage failure scenarios where no equity present.
|
|
// todo: - Move all creation and transmission of statistics out of primary engine loop.
|
|
// todo: - Statistics.Generate(algorithm, resulthandler, transactionhandler);
|
|
|
|
try
|
|
{
|
|
var charts = new Dictionary<string, Chart>(ResultHandler.Charts);
|
|
var orders = new Dictionary<int, Order>(TransactionHandler.Orders);
|
|
var holdings = new Dictionary<string, Holding>();
|
|
var statistics = new Dictionary<string, string>();
|
|
var banner = new Dictionary<string, string>();
|
|
|
|
try
|
|
{
|
|
//Generates error when things don't exist (no charting logged, runtime errors in main algo execution)
|
|
var equity = charts["Strategy Equity"].Series["Equity"].Values;
|
|
var performance = charts["Daily Performance"].Series["Performance"].Values;
|
|
var profitLoss = new SortedDictionary<DateTime,decimal>(algorithm.Transactions.TransactionRecord);
|
|
statistics = Statistics.Statistics.Generate(equity, profitLoss, performance, SetupHandler.StartingCapital, 252);
|
|
}
|
|
catch (Exception err) {
|
|
Log.Error("Algorithm.Node.Engine(): Error generating result packet: " + err.Message);
|
|
}
|
|
|
|
//Diagnostics Completed:
|
|
ResultHandler.DebugMessage("Algorithm Id:(" + job.AlgorithmId + ") completed analysis in " + timer.Elapsed.TotalSeconds.ToString("F2") + " seconds");
|
|
|
|
//Send the result packet:
|
|
ResultHandler.SendFinalResult(job, orders, algorithm.Transactions.TransactionRecord, holdings, statistics, banner);
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
Log.Error("Engine.Main(): Error sending analysis result: " + err.Message + " ST >> " + err.StackTrace);
|
|
}
|
|
|
|
//Before we return, send terminate commands to close up the threads
|
|
timer.Stop(); //Algorithm finished running.
|
|
TransactionHandler.Exit();
|
|
DataFeed.Exit();
|
|
RealTimeHandler.Exit();
|
|
AlgorithmManager.ResetManager();
|
|
}
|
|
|
|
//Close result handler:
|
|
ResultHandler.Exit();
|
|
|
|
//Wait for the threads to complete:
|
|
Log.Trace("Engine.Main(): Waiting for threads to deactivate...");
|
|
var ts = Stopwatch.StartNew();
|
|
while ((ResultHandler.IsActive || (TransactionHandler != null && TransactionHandler.IsActive) || (DataFeed != null && DataFeed.IsActive)) && ts.ElapsedMilliseconds < 60 * 1000)
|
|
{
|
|
Thread.Sleep(100);
|
|
DataFeed.Exit();
|
|
Log.Trace("WAITING >> Result: " + ResultHandler.IsActive + " Transaction: " + TransactionHandler.IsActive + " DataFeed: " + DataFeed.IsActive + " RealTime: " + RealTimeHandler.IsActive);
|
|
}
|
|
|
|
Log.Trace("Engine.Main(): Closing Threads...");
|
|
if (threadFeed != null && threadFeed.IsAlive) threadFeed.Abort();
|
|
if (threadTransactions != null && threadTransactions.IsAlive) threadTransactions.Abort();
|
|
if (threadResults != null && threadResults.IsAlive) threadResults.Abort();
|
|
Log.Trace("Engine.Main(): Analysis Completed and Results Posted.");
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
Log.Error("Engine.Main(): Error running algorithm: " + err.Message + " >> " + err.StackTrace);
|
|
}
|
|
finally
|
|
{
|
|
//Delete the message from the queue before another worker picks it up:
|
|
Tasks.AcknowledgeTask(job);
|
|
Log.Trace("Engine.Main(): Packet removed from queue: " + job.AlgorithmId);
|
|
GC.Collect();
|
|
}
|
|
|
|
//If we're running locally will execute just once.
|
|
} while (!IsLocal);
|
|
|
|
// Send the exit signal and then kill the thread
|
|
StateCheck.Ping.Exit();
|
|
|
|
// Make the console window pause so we can read log output before exiting and killing the application completely
|
|
System.Console.ReadKey();
|
|
|
|
//Finally if ping thread still not complete, kill.
|
|
if (statusPingThread != null && statusPingThread.IsAlive) statusPingThread.Abort();
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Algorithm status monitor reads the central command directive for this algorithm/backtest. When it detects
|
|
/// the backtest has been deleted or cancelled the backtest is aborted.
|
|
/// </summary>
|
|
public static class StateCheck
|
|
{
|
|
/// DB Ping Class
|
|
public static class Ping
|
|
{
|
|
// set to true to break while loop in Run()
|
|
private static bool _exitTriggered;
|
|
|
|
/// DB Ping Run Method:
|
|
public static void Run()
|
|
{
|
|
//Don't run at all if local.
|
|
if (_local) return;
|
|
|
|
while (!_exitTriggered)
|
|
{
|
|
if (AlgorithmManager.AlgorithmId != "" && AlgorithmManager.QuitState == false)
|
|
{
|
|
try
|
|
{
|
|
//Get the state from the central server:
|
|
var state = Controls.GetAlgorithmStatus(AlgorithmManager.AlgorithmId);
|
|
AlgorithmManager.SetStatus(state);
|
|
|
|
Log.Debug("StateCheck.Ping.Run(): Algorithm Status: " + state);
|
|
}
|
|
catch
|
|
{
|
|
Log.Debug("StateCheck.Run(): Error in state check.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log.Debug("StateCheck.Ping.Run(): Opted to not ping: " + AlgorithmManager.AlgorithmId + " " + AlgorithmManager.QuitState);
|
|
}
|
|
Thread.Sleep(1000);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send an exit signal to the thread
|
|
/// </summary>
|
|
public static void Exit()
|
|
{
|
|
_exitTriggered = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Get an instance of the data feed handler we're requesting for this work.
|
|
/// </summary>
|
|
/// <param name="algorithm">User algorithm to scan for securities</param>
|
|
/// <param name="job">Algorithm Node Packet</param>
|
|
/// <param name="brokerage">Brokerage instance to avoid access token duplication</param>
|
|
/// <returns>Class matching IDataFeed Interface</returns>
|
|
private static IDataFeed GetDataFeedHandler(IAlgorithm algorithm, IBrokerage brokerage, AlgorithmNodePacket job)
|
|
{
|
|
var df = default(IDataFeed);
|
|
switch (job.DataEndpoint)
|
|
{
|
|
//default:
|
|
////Backtesting:
|
|
case DataFeedEndpoint.Backtesting:
|
|
df = new BacktestingDataFeed(algorithm, (BacktestNodePacket)job);
|
|
Log.Trace("Engine.GetDataFeedHandler(): Selected Backtesting Datafeed");
|
|
break;
|
|
|
|
//Operation from local files:
|
|
case DataFeedEndpoint.FileSystem:
|
|
df = new FileSystemDataFeed(algorithm, (BacktestNodePacket)job);
|
|
Log.Trace("Engine.GetDataFeedHandler(): Selected FileSystem Datafeed");
|
|
break;
|
|
|
|
//Tradier Data Source:
|
|
case DataFeedEndpoint.Tradier:
|
|
df = new TradierDataFeed(algorithm, brokerage, (LiveNodePacket)job);
|
|
Log.Trace("Engine.GetDataFeedHandler(): Selected Tradier Datafeed");
|
|
break;
|
|
}
|
|
return df;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Select the realtime event handler set in the job.
|
|
/// </summary>
|
|
private static IRealTimeHandler GetRealTimeHandler(IAlgorithm algorithm, IBrokerage brokerage, IDataFeed feed, IResultHandler results, AlgorithmNodePacket job)
|
|
{
|
|
var rth = default(IRealTimeHandler);
|
|
switch (job.RealTimeEndpoint)
|
|
{
|
|
//Don't fire based on system time but virtualized backtesting time.
|
|
case RealTimeEndpoint.Backtesting:
|
|
Log.Trace("Engine.GetRealTimeHandler.GetRealTimeHandler(): Selected Backtesting RealTimeEvent Handler");
|
|
rth = new BacktestingRealTimeHandler(algorithm, job);
|
|
break;
|
|
|
|
// Fire events based on real system clock time.
|
|
case RealTimeEndpoint.LiveTrading:
|
|
Log.Trace("Engine.GetRealTimeHandler.GetRealTimeHandler(): Selected LiveTrading RealTimeEvent Handler");
|
|
rth = new LiveTradingRealTimeHandler(algorithm, feed, results, brokerage, job);
|
|
break;
|
|
}
|
|
return rth;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Get an instance of the transaction handler set by the task.
|
|
/// </summary>
|
|
/// <param name="algorithm">Algorithm instance</param>
|
|
/// <param name="job">Algorithm job packet</param>
|
|
/// <param name="brokerage">Brokerage instance to avoid access token duplication</param>
|
|
/// <param name="results">Results array for sending order events.</param>
|
|
/// <returns>Class matching ITransactionHandler interface</returns>
|
|
private static ITransactionHandler GetTransactionHandler(IAlgorithm algorithm, IBrokerage brokerage, IResultHandler results, AlgorithmNodePacket job)
|
|
{
|
|
ITransactionHandler th;
|
|
switch (job.TransactionEndpoint)
|
|
{
|
|
//Operation from local files:
|
|
default:
|
|
th = new BacktestingTransactionHandler(algorithm);
|
|
Log.Trace("Engine.GetTransactionHandler(): Selected Backtesting Transaction Models.");
|
|
break;
|
|
|
|
case TransactionHandlerEndpoint.Tradier:
|
|
var live = job as LiveNodePacket;
|
|
Log.Trace("Engine.GetTransactionHandler(): Selected Live Transaction Fills.");
|
|
th = new TradierTransactionHandler(algorithm, brokerage, results, live.AccountId);
|
|
break;
|
|
}
|
|
return th;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get an instance of the data feed handler we're requesting for this work.
|
|
/// </summary>
|
|
/// <param name="job">Algorithm Node Packet</param>
|
|
/// <returns>Class Matching IResultHandler Inteface</returns>
|
|
private static IResultHandler GetResultHandler(AlgorithmNodePacket job)
|
|
{
|
|
var rh = default(IResultHandler);
|
|
switch (job.ResultEndpoint)
|
|
{
|
|
//Local backtesting and live trading result handler route messages to the local console.
|
|
case ResultHandlerEndpoint.Console:
|
|
Log.Trace("Engine.GetResultHandler(): Selected Console Output.");
|
|
rh = new ConsoleResultHandler((BacktestNodePacket)job);
|
|
break;
|
|
|
|
|
|
// Backtesting route messages to user browser.
|
|
case ResultHandlerEndpoint.Backtesting:
|
|
Log.Trace("Engine.GetResultHandler(): Selected Backtesting API Result Endpoint.");
|
|
rh = new BacktestingResultHandler((BacktestNodePacket)job);
|
|
break;
|
|
|
|
// Live trading route messages to user's browser.
|
|
case ResultHandlerEndpoint.LiveTrading:
|
|
Log.Trace("Engine.GetResultHandler(): Selected Live Trading API Result Endpoint.");
|
|
rh = new LiveTradingResultHandler((LiveNodePacket)job);
|
|
break;
|
|
}
|
|
return rh;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Get the setup handler for this algorithm, depending on its use case.
|
|
/// </summary>
|
|
/// <param name="setupMethod">Setup handler</param>
|
|
/// <returns>Instance of a setup handler:</returns>
|
|
private static ISetupHandler GetSetupHandler(SetupHandlerEndpoint setupMethod)
|
|
{
|
|
var sh = default(ISetupHandler);
|
|
if (IsLocal) return new ConsoleSetupHandler();
|
|
|
|
switch (setupMethod)
|
|
{
|
|
//Setup console handler:
|
|
case SetupHandlerEndpoint.Console:
|
|
sh = new ConsoleSetupHandler();
|
|
Log.Trace("Engine.GetSetupHandler(): Selected Console Algorithm Setup Handler.");
|
|
break;
|
|
//Default, backtesting result handler:
|
|
case SetupHandlerEndpoint.Backtesting:
|
|
sh = new BacktestingSetupHandler();
|
|
Log.Trace("Engine.GetSetupHandler(): Selected Backtesting Algorithm Setup Handler.");
|
|
break;
|
|
case SetupHandlerEndpoint.PaperTrading:
|
|
sh = new PaperTradingSetupHandler();
|
|
Log.Trace("Engine.GetSetupHandler(): Selected PaperTrading Algorithm Setup Handler.");
|
|
break;
|
|
case SetupHandlerEndpoint.Tradier:
|
|
sh = new TradierSetupHandler();
|
|
Log.Trace("Engine.GetSetupHandler(): Selected Tradier Algorithm Setup Handler.");
|
|
break;
|
|
}
|
|
return sh;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Setup a private directory structure, clean the caches:
|
|
/// </summary>
|
|
private static void CleanUpDirectories()
|
|
{
|
|
var baseCache = Directory.GetCurrentDirectory() + "/cache";
|
|
string[] resolutions = { "minute", "second", "tick" };
|
|
var days = 30;
|
|
var filesDeleted = 0;
|
|
|
|
try
|
|
{
|
|
if (!Directory.Exists(baseCache)) {
|
|
//Create new directory structure fresh:
|
|
Directory.CreateDirectory(baseCache + @"/algorithm");
|
|
Directory.CreateDirectory(baseCache + @"/data");
|
|
}
|
|
|
|
while (OS.DriveSpaceRemaining < 3000 && !IsLocal && days-- >= 0)
|
|
{
|
|
//Clean out the current structure each loop:
|
|
var files = Directory.GetFiles(baseCache + @"/algorithm");
|
|
foreach (var file in files)
|
|
{
|
|
File.Delete(file);
|
|
filesDeleted++;
|
|
}
|
|
|
|
//Delete the user custom data:
|
|
files = Directory.GetFiles(baseCache + @"/data");
|
|
foreach (var file in files)
|
|
{
|
|
var info = new FileInfo(file);
|
|
if (info.LastAccessTime < DateTime.Now.AddDays(days)) File.Delete(file);
|
|
}
|
|
|
|
//Go through all the resolutions and symbols, and
|
|
foreach (var resolution in resolutions)
|
|
{
|
|
var directoryLocation = baseCache + @"/data/equity/" + resolution + @"/";
|
|
if (!Directory.Exists(directoryLocation)) Directory.CreateDirectory(directoryLocation);
|
|
|
|
var directories = Directory.GetDirectories(directoryLocation);
|
|
foreach (var directory in directories)
|
|
{
|
|
files = Directory.GetFiles(directory);
|
|
foreach (var file in files)
|
|
{
|
|
var info = new FileInfo(file);
|
|
if (info.LastAccessTime < DateTime.Now.AddDays(-days))
|
|
{
|
|
File.Delete(file);
|
|
filesDeleted++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Log.Trace("Engine.CleanUpDirectory(): Cleared " + filesDeleted + " files from cache. " + OS.DriveSpaceRemaining + " MB Disk Remaining");
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
//Error cleaning up the directories
|
|
Log.Error("Engine.CleanUpDirectories(): " + err.Message);
|
|
}
|
|
}
|
|
|
|
} // End Algorithm Node Core Thread
|
|
|
|
} // End Namespace
|