Skip to content

Instantly share code, notes, and snippets.

@Jay-Jay-D
Created March 16, 2017 15:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jay-Jay-D/63ab5838eb24aff0b665b5889db3997c to your computer and use it in GitHub Desktop.
Save Jay-Jay-D/63ab5838eb24aff0b665b5889db3997c to your computer and use it in GitHub Desktop.
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Securities;
using System;
using System.Collections.Generic;
using System.Linq;
namespace QuantConnect.Algorithm.CSharp
{
public class RiskManagerTestingAlgorithm : QCAlgorithm
{
private decimal _maxExposure = 0.8m;
private decimal _maxExposurePerTrade = 0.3m;
private decimal _riskPerTrade = 0.02m;
bool _runMarginTest = true;
private List<Identity> priceIndicators;
private FxRiskManagment RiskManager;
public override void Initialize()
{
SetStartDate(2008, 11, 17); //Set Start Date
SetEndDate(2008, 11, 28); //Set End Date
SetCash(10000); //Set Strategy Cash
AddForex("USDJPY", Resolution.Daily, market: "oanda", leverage: 10);
AddForex("EURUSD", Resolution.Daily, market: "oanda", leverage: 10);
var gbpResolution = LiveMode ? Resolution.Minute : Resolution.Daily;
AddForex("GBPUSD", gbpResolution, market: "oanda", leverage: 20);
priceIndicators = new List<Identity>();
foreach (var pair in Securities.Keys)
{
Securities[pair].FeeModel = new ConstantFeeModel(0m);
Securities[pair].FillModel = new ImmediateFillModel();
var vol = 0.01m * (pair.Value == "USDJPY" ? 100 : 1);
Securities[pair].VolatilityModel = new ConstantVolatilityModel(vol);
priceIndicators.Add(Identity(pair));
}
RiskManager = new FxRiskManagment(Portfolio, _riskPerTrade, _maxExposurePerTrade, _maxExposure);
}
public override void OnData(Slice slice)
{
Tuple<int, decimal> entryOrders;
decimal stopLossPrice;
if (!LiveMode)
{
#region Test: if the used margin is bigger than the max exposure, then the entry order quantity must be zero.
// Buy EURUSD beyond the max exposure.
if (slice.Time.Day == 17)
{
MarketOrder("EURUSD", 65000);
}
if (slice.Time.Day == 18)
{
// Test 1: if the used margin is bigger than the max exposure, then the entry order quantity must be zero.
entryOrders = RiskManager.CalculateEntryOrders("EURUSD", AgentAction.GoLong);
if (entryOrders.Item1 != 0)
{
throw new Exception("The RiskManager allows operations beyond the max exposure.");
}
// Clean Stuff and set up the next test.
Liquidate("EURUSD");
}
#endregion Test: if the used margin is bigger than the max exposure, then the entry order quantity must be zero.
#region Tests: Happy path.
if (slice.Time.Day == 19)
{
Portfolio.SetCash(10000m);
// Test 2: happy path with a long entry with USD as base currency.
entryOrders = RiskManager.CalculateEntryOrders("EURUSD", AgentAction.GoLong);
stopLossPrice = slice["EURUSD"].Close - Securities["EURUSD"].VolatilityModel.Volatility;
if (entryOrders.Item1 != 20000 || entryOrders.Item2 != stopLossPrice)
{
throw new Exception("Quantity or StopLoss price estimated incorrectly when USD is the base currency.");
}
// Test 3: estimate a short entry orders with JPY as base currency.
entryOrders = RiskManager.CalculateEntryOrders("USDJPY", AgentAction.GoShort);
stopLossPrice = slice["USDJPY"].Close + Securities["USDJPY"].VolatilityModel.Volatility;
if (entryOrders.Item1 != -19000 || entryOrders.Item2 != stopLossPrice)
{
throw new Exception("Quantity or StopLoss price estimated incorrectly when USD is not the base currency.");
}
// Send the order and set the thing up for the next tests.
MarketOrder("USDJPY", entryOrders.Item1);
// Stop price high to avoid execution.
StopMarketOrder("USDJPY", -entryOrders.Item1, 98);
}
if (slice.Time.Day == 20)
{
// Test 4: estimate a new long entry order with USD as base currency.
entryOrders = RiskManager.CalculateEntryOrders("EURUSD", AgentAction.GoLong);
stopLossPrice = slice["EURUSD"].Close - Securities["EURUSD"].VolatilityModel.Volatility;
if (entryOrders.Item1 != 21000 || entryOrders.Item2 != stopLossPrice)
{
throw new Exception("Quantity or StopLoss price estimated incorrectly when USD is the base currency.");
}
MarketOrder("EURUSD", entryOrders.Item1);
StopMarketOrder("EURUSD", -entryOrders.Item1, entryOrders.Item2);
}
#endregion Tests: Happy path.
#region Test: Update trailing orders.
if (slice.Time.Day == 21)
{
// Test 5: update trailing orders
RiskManager.UpdateTrailingStopOrders();
var tickets = Transactions.GetOrderTickets(o => o.OrderType == OrderType.StopMarket && o.Status == OrderStatus.Submitted);
foreach (var ticket in tickets)
{
var actualStopLossPrice = ticket.UpdateRequests.First().StopPrice;
var expectedStopLossPrice = Securities[ticket.Symbol].Price +
(Securities[ticket.Symbol].VolatilityModel.Volatility * (ticket.Quantity < 0 ? -1 : 1));
if (actualStopLossPrice != expectedStopLossPrice) throw new Exception("Trailing stop loss fail.");
}
// Clean all stuff for the next test.
Liquidate("EURUSD");
Liquidate("USDJPY");
}
#endregion Test: Update trailing orders.
}
#region Test: Margin use and Leverage.
var testDay = 23;
if (LiveMode) testDay = DateTime.Today.Day;
if (slice.Time.Day == testDay && _runMarginTest)
{
// Get the actual portfolio value
var marginPreOrder = Portfolio.MarginRemaining;
entryOrders = RiskManager.CalculateEntryOrders("GBPUSD", AgentAction.GoLong);
var orderQuantity = entryOrders.Item1;
var pairLeverage = Securities["GBPUSD"].Leverage;
var pairRate = Securities["GBPUSD"].Price;
var expectedMarginPostOrder = marginPreOrder - (orderQuantity * pairRate) / pairLeverage;
var orderTicket = MarketOrder("GBPUSD", entryOrders.Item1);
if (LiveMode)
{
do
{ } while (orderTicket.Status == OrderStatus.Filled);
}
// The difference can be one because of the default fees.
var actualdMarginPostOrder = Portfolio.MarginRemaining;
var difference = Math.Abs((double)(actualdMarginPostOrder - expectedMarginPostOrder));
var differenceTolerance = 5;
if (difference > differenceTolerance)
{
Liquidate("GBPUSD");
var errorMsg = string.Format(@"Expected margin post order fill: {0}\nActual margin: {1}.\nDifference: {2}",
expectedMarginPostOrder, actualdMarginPostOrder, difference);
throw new Exception(errorMsg);
}
_runMarginTest = false;
Liquidate("GBPUSD");
}
#endregion Test: Margin use and Leverage.
}
internal class ConstantVolatilityModel : IVolatilityModel
{
private decimal volatility;
public ConstantVolatilityModel(decimal v)
{
this.volatility = v;
}
public decimal Volatility
{
get
{
return volatility;
}
}
public IEnumerable<HistoryRequest> GetHistoryRequirements(Security security, DateTime utcTime)
{
return Enumerable.Empty<HistoryRequest>();
}
public void Update(Security security, BaseData data)
{ }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment