#region Namespaces
using System;
using System.IO;
using System.Linq;
#endregion
namespace ScriptCode {
/// <summary>
/// Risk management scripts are used for managing risk by modifying or cancelling trading strategy orders based on portfolio level risk analysis.
///
/// This script can be used in several ways:
/// (1) It can be used to manage risk by providing a bird's eye view of the entire desktop, including all of its strategies.
/// (2) It can be used to limit overexposure to a single symbol due to multiple trading strategies that are trading it.
/// (3) It can be used to manage risk during market meltdowns or during extreme volatility.
/// </summary>
public partial class MyRiskManagement : RiskManagementScriptBase // NEVER CHANGE THE CLASS NAME
{
#region Variables
// The maximum percent of the equity that a single symbol can be allocated.
private double _maxPercent;
// The currency code the equity is denominated in.
private string _currencyCode;
#endregion
#region OnInitialize
/// <summary>
/// This function is used for accepting the script parameters and for initializing the script prior to all other function calls.
/// Once the script is assigned to a desktop, its parameter values can be specified by the user and can be selected for optimization.
/// </summary>
/// --------------------------------------------------------------------------------------------------
/// PLEASE USE THE SCRIPT WIZARD (CTRL+W) TO ADD, EDIT AND REMOVE THE SCRIPT PARAMETERS
/// --------------------------------------------------------------------------------------------------
/// YOU MUST SET A PARAM TAG FOR EACH PARAMETER ACCEPTED BY THIS FUNCTION.
/// ALL PARAM TAGS SHOULD BE SET IN THE 'OnInitialize' REGION, RIGHT ABOVE THE 'OnInitialize' FUNCTION.
/// THE ORDER OF THE TAGS MUST MATCH THE ORDER OF THE ACTUAL PARAMETERS.
/// REQUIRED ATTRIBUTES:
/// (1) name: The exact parameter name.
/// (2) type: The type of data to collect from the user:
/// Set to "Integer" when the data type is 'int'
/// Set to "IntegerArray" when the data type is 'int[]'
/// Set to "DateTime" when the data type is 'long'
/// Set to "DateTimeArray" when the data type is 'long[]'
/// Set to "Boolean" when the data type is 'bool'
/// Set to "BooleanArray" when the data type is 'bool[]'
/// Set to "Double" when the data type is 'double'
/// Set to "DoubleArray" when the data type is 'double[]'
/// Set to "String" when the data type is 'string'
/// Set to "StringArray" when the data type is 'string[]'
/// OPTIONAL ATTRIBUTES:
/// (3) default: The default parameter value is only valid when the type is Integer, Boolean, Double, String or an API Type.
/// (4) min: The minimum parameter value is only valid when the type is Integer or Double.
/// (5) max: The maximum parameter value is only valid when the type is Integer or Double.
/// EXAMPLE: <param name="" type="" default="" min="" max="">Enter the parameter description here.</param>
/// --------------------------------------------------------------------------------------------------
/// <param name="maxPercent" type="Double" default="2">Use for the maximum percent of the equity that a single symbol can be allocated.</param>
/// <param name="currencyCode" type="String" default="USD">Use for the currency code the equity is denominated in.</param>
public void OnInitialize(
double maxPercent,
string currencyCode) {
// Set the script parameter to a script variable.
_maxPercent = maxPercent;
_currencyCode = currencyCode;
}
#endregion
#region OnRiskManagement
/// <summary>
/// This function is called for each new pending order so that it may modify or cancel it if necessary.
/// </summary>
/// <param name="strategyNumber" type="Integer">The strategy number to which the order belongs</param>
/// <param name="orderIndex" type="Integer">The order index in the orders table</param>
public override void OnRiskManagement(
int strategyNumber,
int orderIndex) {
// Check whether the order is an exit order which should never be cancelled as it decreases risk.
if (OrderActionType(strategyNumber, orderIndex) == C_ActionType.BUY_TO_COVER ||
OrderActionType(strategyNumber, orderIndex) == C_ActionType.SELL)
return ;
// The max amount of equity per trade.
double maxEquity = 0;
// Iterate over all of the strategies.
for (int i = 0; i < StrategyCount(); i++) {
// Check whether the strategy is active.
if (StrategyIsActive(i)) {
// Sum the max equity.
maxEquity += StrategyGetExchangeRate(StrategyCurrencyCode(i), _currencyCode) * StrategyEquity(i);
}
}
// Calculate the max equity as a percent from the sum of all the strategy equities (in the specified currency).
maxEquity = maxEquity * (_maxPercent / 100);
// Get the specified order's symbol index.
int symbolIndex = OrderSymbolIndex(strategyNumber, orderIndex);
// Get the underlying symbol code for the order symbol.
string symbolCode = SymbolID(strategyNumber, symbolIndex);
// Get the exchange rate between the symbol and the specified currency code.
double exchangeRate = StrategyGetExchangeRate(SymbolCurrencyCode(strategyNumber, symbolIndex), _currencyCode);
// The total symbol equity;
double symbolEquity = 0;
// Iterate over all of the strategies.
for (int i = 0; i < StrategyCount(); i++) {
// Check whether the current strategy is active.
if (StrategyIsActive(i)) {
// The symbol index of the order's symbol in the current strategy (the strategy might not have this symbol at all).
symbolIndex = -1;
// Iterate over all of the current strategy's symbols while searching for a matching symbol.
for (int j = 0; j < SymbolCount(i); j++) {
// Check whether a matching symbol has been found in the current strategy.
if (SymbolID(i, j) == symbolCode)
{
// Keep the symbol index of the order's symbol in the current strategy.
symbolIndex = j;
break;
}
}
// Check whether a matching symbol has been found.
if (symbolIndex >= 0) {
// Get the pending orders of the order symbol in the current strategy.
int[] pendingOrderIndexes = OrderByStatus(i, C_Status.PENDING, symbolIndex);
// Iterate over all of the pending orders.
for (int j = 0; j < pendingOrderIndexes.Length; j++) {
// Check whether the pending order is an entry order.
if (OrderActionType(i, pendingOrderIndexes[j]) == C_ActionType.BUY ||
OrderActionType(i, pendingOrderIndexes[j]) == C_ActionType.SELL_SHORT)
// Add the expected equity of the current pending order.
symbolEquity += exchangeRate * OrderExpectedPrice(i, pendingOrderIndexes[j]) * OrderQuantity(i, pendingOrderIndexes[j]);
}
// Get the open position indexes of the specifieed symbol in the current strategy.
int[] openPositionIndexes = PositionByStatus(i, C_PositionStatus.OPEN, symbolIndex);
// Iterate over all of the open position indexes.
for (int j = 0; j < openPositionIndexes.Length; j++) {
// Add the equity of the current position.
symbolEquity += exchangeRate * PositionCurrentValue(i, openPositionIndexes[j]);
}
}
}
}
// Check whether the symbol equity is too much.
if (symbolEquity >= maxEquity) {
BrokerCancelOrder(strategyNumber, orderIndex, "Risk management, max equity limit.");
}
}
#endregion
#region OnShutdown
/// <summary>
/// This function is called when the script is shutdown.
/// </summary>
public override void OnShutdown() {
}
#endregion
}
}