ロジックとしてはそれなりに動きます。
//+------------------------------------------------------------------+
//| MTR20250309.mq4 |
//| Copyright 2025, bakueki-fx.com |
//| https://bakueki-fx.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, bakueki-fx.com"
#property link "https://bakueki-fx.com"
#property version "1.00"
#property strict
//--- input parameters
// バックテスト時は、timerInterval = 0 にしないと動かない。
// トレンドフォロー時、stopOrderSpreadPips は、 doOrderRangePips 未満にしないとエントリーしない。
// backTestMode = false では、処理が重くバックテストに時間がかかる。
input string eaName = "MTR20250309";
input int timerInterval = 10;
input int magicNumber = 1110000;
input double lots = 0.01;
input int expirationHour=24;
input bool isBuyTrap=true;
input double maxOpenPrice = 160;
input int trapCount = 600;
input int trapIntervalPips = 10;
input int doOrderRangePips = 50;
input bool isTrendFollowOnly = false;
input int stopOrderSpreadPips = 10;
input double allTakeProfit=0;
input int limitProfitPips = 0;
input int minProfitPips = 10;
input int trailingProfitPips = 5;
input double maxTakeLots=3;
input int printInfomationLevel=0;
//input bool doPrintInformation = true;
//input double lossCutBorderLine = 100;
input int intervalTrapOrder = 10;
input int intervalTrailingStop = 20;
input bool backTestMode = false;
//--- Global parameters
int waitTime = 5; // 待機時間
int slippage = 3; // スリッページ
int roundDown=1000;
color arrowColor[6] = { Green, Red, Green, Red, Green, Red }; // 矢印の色
int ticketArray[]; // チケット配列
int magicArray[]; // マジックナンバー配列
datetime lastExecutionTrapOrder = 0;
datetime lastExecutionTrailingStop = 0;
double pointPerPips; // 1pipsあたりのポイント
int slippagePips;
int digitMultiplier;
double buySum = 0; // Buyポジションの加重取得価格合計
double sellSum = 0; // Sellポジションの加重取得価格合計
double buyLotSum = 0; // Buyポジションのロット数合計
double sellLotSum = 0; // Sellポジションのロット数合計
double buyAverage = 0; // Buyポジションの平均取得価格
double sellAverage = 0; // Sellポジションの平均取得価格
double buyLossCutLine =0 ;
double sellLossCutLine =0 ;
int buyPositionCount = 0;
int sellPositionCount =0;
double buyLotProfit = 0; // Buyポジションの利益合計
double sellLotProfit = 0; // Sellポジションの利益合計
double magicAllProfit=0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
int mult = 1;
if(Digits == 3 || Digits == 5) {
mult = 10;
}
pointPerPips = Point * mult;
slippagePips = slippage * mult;
digitMultiplier = MathPow(10,Digits);
Print(" Digits= ",Digits," Point= ",Point," pointPerPips= ",pointPerPips," slippagePips= ",slippagePips," digitMultiplier= ",digitMultiplier," mult= ",mult );
//--- create timer
if (timerInterval > 0)
{
EventSetTimer(timerInterval); // 外部入力のタイマー間隔を設定
Print("Timer enabled with interval: ", timerInterval, " seconds.");
}
else
{
Print("Timer disabled. Using OnTick() instead.");
}
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- destroy timer
EventKillTimer();
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//---
if (timerInterval <= 0)
{
main(); // TimerIntervalが0以下の場合、OnTick()で動作
}
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
//---
if (timerInterval > 0)
{
main(); // TimerIntervalが有効の場合、OnTimerで動作
}
}
//+------------------------------------------------------------------+
//| main関数 |
//+------------------------------------------------------------------+
void main()
{
//---
/*
if (!backTestMode==true){
if(initOrderTicketInfo()==false)return;
doGetInfomation();
}
*/
datetime nowO = TimeCurrent();
datetime nowT = TimeCurrent()+10;
if(nowO - lastExecutionTrapOrder >= intervalTrapOrder){
if (backTestMode==false)doGetInfomation();
if(initOrderTicketInfo()==false)return;
if (printInfomationLevel>=2){
Print("MagicNumber= ",magicNumber," トラップ発注を実行 時間= ",nowO);
}
// doGetInfomation();
doTrapRepeatIfDone();
lastExecutionTrapOrder =nowO;
}
if(nowT - lastExecutionTrailingStop >= intervalTrailingStop){
getMagicProfit();
// doGetInfomation();
if (limitProfitPips==0){
if(initOrderTicketInfo()==false)return;
if (printInfomationLevel>=2){
Print("MagicNumber= ",magicNumber," トレーリングストップを実行 時間= ",nowT);
}
doTrailing();
}
else{
if (printInfomationLevel>=2){
Print("MagicNumber= ",magicNumber," トレーリングストップを実行 時間= ",nowT,"limitProfitPips==0 にてトレーリングは実行されません。");
}
lastExecutionTrailingStop =nowT;
}
}
}
//+------------------------------------------------------------------+
bool initOrderTicketInfo(){
// 対象通貨ペアの件数を取得
int orderCount=0;
for(int j=0; j < OrdersTotal(); j++) {
if(OrderSelect(j,SELECT_BY_POS,MODE_TRADES) == false) return(false);
if(OrderSymbol() == Symbol()) {
orderCount++;
}
}
// 配列を初期化
ArrayResize(ticketArray,orderCount);
ArrayResize(magicArray,orderCount);
ArrayInitialize(ticketArray, -1); // 初期化
ArrayInitialize(magicArray, -1); // 初期化
orderCount=0;
// 配列にセット
for(int j=0; j < OrdersTotal(); j++) {
if(OrderSelect(j,SELECT_BY_POS,MODE_TRADES) == false) return(false);
if(OrderSymbol() == Symbol()) {
ticketArray[orderCount] = OrderTicket();
magicArray[orderCount] = OrderMagicNumber();
orderCount++;
}
}
return(true);
}
//+------------------------------------------------------------------+
// 指定されたMAGIC注文があるかどうかをチェックする。なければ「-1」を返す
//
int getOrderTicket(int magic){
int orderCount = 0;
int ticket = -1;
for(int i=0; i < ArraySize(magicArray); i++) {
if(magic == magicArray[i]) {
ticket = ticketArray[i];
orderCount++;
}
}
if(orderCount > 1){
}
return(ticket);
}
//+------------------------------------------------------------------+
//
int doTrapRepeatIfDone() {
for(int i=0; i < trapCount; i++){
double openPrice = maxOpenPrice - trapIntervalPips * i * pointPerPips;
// 現値より大きく乖離した発注は出さない
double currentPrice;
if(isBuyTrap){
currentPrice = Ask;
} else {
currentPrice = Bid;
}
if( doOrderRangePips != 0 &&
(openPrice < currentPrice - doOrderRangePips * pointPerPips || openPrice > currentPrice + doOrderRangePips * pointPerPips) ){
continue;
}
double closePrice;
if( limitProfitPips !=0 ){
if(isBuyTrap == true){
closePrice = openPrice + limitProfitPips * pointPerPips;
} else {
closePrice = openPrice - limitProfitPips * pointPerPips;
}
}
else{
closePrice=limitProfitPips;
}
int magic = magicNumber+i;
// 注文状態をチェックし、注文がなければリピートする
int ticket = getOrderTicket(magic);
if ( ticket <= 0) {
ticket = doRepeatIfDone(openPrice,closePrice, magic);
}
}
return(0);
}
// 指定された条件でリピートIfDone注文を実行する
int doRepeatIfDone(double openPrice,double closePrice,int magic) {
// 注文形式を決定する
int tradeType = -1;
if( isTrendFollowOnly == true ) {
// トレンドフォローモードの場合、トレンドフォローを確認するため、
// ASK値が一旦、閾値以上の下値に達した後に建値まで戻ってきた場合にのみ
// リピートするようにする
// stopOrderSpreadPipsの値は、doOrderRangePipsより小さい値でないと発注しない!
if(isBuyTrap && openPrice >= Ask + stopOrderSpreadPips * pointPerPips ) {
tradeType = OP_BUYSTOP;
} else if(!isBuyTrap && openPrice < Bid - stopOrderSpreadPips * pointPerPips ) {
tradeType = OP_SELLSTOP;
}
} else {
if(isBuyTrap && openPrice <= Ask) {
// 買い注文で、建値 <= 現在価格(Ask) の場合は買い指値
tradeType = OP_BUYLIMIT;
} else if(isBuyTrap && openPrice > Ask) {
// 買い注文で、建値 > 現在価格(Ask) の場合は買い逆指値
tradeType = OP_BUYSTOP;
} else if(!isBuyTrap && openPrice >= Bid) {
// 売り注文で、建値 >= 現在価格(Bid) の場合は売り指値
tradeType = OP_SELLLIMIT;
} else if(!isBuyTrap && openPrice < Bid) {
// 売り注文で、建値 < 現在価格(Bid) の場合は売り逆指値
tradeType = OP_SELLSTOP;
}
}
// 注文形式が注文不要の場合はそのまま終了する
if(tradeType == -1) {
return(-1);
}
int ticket = doOrderSend(tradeType,openPrice,closePrice,magic);
return(ticket);
}
//+------------------------------------------------------------------+
//
// 発注する
int doOrderSend(int type, double openPrice,double closePrice, int magic) {
// 0時23時は動作しない。
if(Hour()==23||Hour()==0||DayOfWeek()==6||DayOfWeek()==0){
return(-1);
}
doGetInfomation();
/*
if (!backTestMode==true){
if( buyPositionCount >= maxTakePosition|| sellPositionCount >= maxTakePosition){
if (printInfomationLevel>=1){
Print("これ以上の発注はできません。 "," BuyPosition= ",buyPositionCount," SellPosition= ",sellPositionCount);
}
return(-1);
}
if(isBuyTrap==true){
if( buyLossCutLine >= lossCutBorderLine){
if (printInfomationLevel>=1||printInfomationLevel==-1){
Print("これ以上の発注はできません。 "," buyLossCutLine= ",buyLossCutLine," lossCutBorderLine= ",lossCutBorderLine," 余剰証拠金= ",AccountFreeMargin()," 買いLot数= ",buyLotSum," 買い平均価格= ",buyAverage);
}
return(-1);
}
}
else{
if( sellLossCutLine <= lossCutBorderLine){
if (printInfomationLevel>=1||printInfomationLevel==-1){
Print("これ以上の発注はできません。 "," sellLossCutLine= ",sellLossCutLine," lossCutBorderLine= ",lossCutBorderLine," 余剰証拠金= ",AccountFreeMargin()," 売りLot数= ",sellLotSum," 売り平均価格= ",sellAverage);
}
return(-1);
}
}
}
*/
if (isBuyTrap && (buyLotSum >= maxTakeLots || sellLotSum >= maxTakeLots)) {
return(-1);
}
openPrice = NormalizeDouble(openPrice, Digits);
datetime expiration = (expirationHour == 0) ? 0 : TimeCurrent() + (expirationHour * 3600);
/*
while(true) {
if(IsTradeAllowed() == true) {
RefreshRates();
int ticket = OrderSend(Symbol(), type, lots, openPrice, slippage, 0, closePrice, magic, magic, expiration, arrowColor[type]);
if( ticket > 0) {
if (printInfomationLevel>=1){
Print("発注しました magic= ",magic ," ticket= ",ticket," openPrice= ",openPrice);
}
Sleep(waitTime*1000);
return(ticket);
}
if (printInfomationLevel>=1){
Print("発注エラー magic= ",magic ," ticket= ",ticket," エラーコード: ", GetLastError());
}
Sleep(waitTime*2000);
break;
}
}
*/
int maxRetries = 4; // リトライ回数の上限
for (int retry = 1; retry < maxRetries; retry++) {
if (IsTradeAllowed()) {
RefreshRates();
int ticket = OrderSend(Symbol(), type, lots, openPrice, slippage, 0, closePrice, magic, magic, expiration, arrowColor[type]);
//int ticket = OrderSend(Symbol(), type, lot, price, slippage, sl, tp);
if (ticket > 0) {
Print("注文成功: Ticket=", ticket);
return (ticket); // 成功したら終了
} else {
Print("注文失敗: エラーコード=", GetLastError());
}
} else {
Print("トレード不可状態、リトライ: ", retry + 1);
}
Sleep(1000*retry); // 1秒待機してリトライ
}
Print("最大リトライ回数到達、注文失敗");
return(-1);
}
//+------------------------------------------------------------------+
//
int doTrailing() {
// 0時23時は動作しない。
if(Hour()==23||Hour()==0||DayOfWeek()==6||DayOfWeek()==0){
return(-1);
}
for(int i=0; i < trapCount; i++){
int magic = magicNumber+i;
// 注文状態をチェックし、注文があれば 実行する。
int ticket = getOrderTicket(magic);
if (ticket > 0) {
if (allTakeProfit>0){
doTrailingOrderModifyAP(ticket);
}
else{
doTrailingOrderModify(ticket);
}
}
}
return(0);
}
//+------------------------------------------------------------------+
//
int doTrailingOrderModify(int ticket){
if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES) ==true)
{
if(OrderType() > 1) {
return(-1);
}
RefreshRates();
double orderDifference = (OrderType() == OP_BUY) ? (Bid - OrderOpenPrice()) : (OrderOpenPrice()-Ask);
orderDifference =orderDifference /pointPerPips;
if(orderDifference < minProfitPips )return(-1);
if (printInfomationLevel>=1){
Print("★ トレール利益を超えた Bid= ",Bid," OrderOpenPrice()= ",OrderOpenPrice()," doTrailingOrderModify ticket= ",ticket," orderDifferenc Point= ",orderDifference);
}
// 現在の逆指値価格を計算
double newStopLoss = (OrderType() == OP_BUY) ? Bid - trailingProfitPips * pointPerPips : Ask + trailingProfitPips * pointPerPips;
// 既存の逆指値を更新
if ((OrderType() == OP_BUY && (OrderStopLoss() < newStopLoss || OrderStopLoss() == 0)) ||
(OrderType() == OP_SELL && (OrderStopLoss() > newStopLoss || OrderStopLoss() == 0)))
{
if (!OrderModify(ticket, 0, NormalizeDouble(newStopLoss, Digits), 0, 0, clrYellow))
// if (!OrderModify(ticket, 0, newStopLoss, 0, 0, clrYellow))
{
if (printInfomationLevel>=1){
Print("OrderModify failed: ", GetLastError(), " OrderTicket: ", OrderTicket(),"OrderMagicNumber= ", OrderMagicNumber());
}
}
else
{
if (printInfomationLevel>=1){
Print("OrderModify succeeded for ticket: ", OrderTicket(), " New StopLoss: ", DoubleToString(newStopLoss, Digits));
}
}
}
}
return(ticket);
}
//+------------------------------------------------------------------+
//
//+------------------------------------------------------------------+
//
int doTrailingOrderModifyAP(int ticket){
if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES) ==true)
{
if(OrderType() > 1) {
return(-1);
}
RefreshRates();
/*
double orderDifference = (OrderType() == OP_BUY) ? (Bid - OrderOpenPrice()) : (OrderOpenPrice()-Ask);
orderDifference =orderDifference /pointPerPips;
if(orderDifference < minProfitPips )return(-1);
*/
if(magicAllProfit < allTakeProfit )return(-1);
if (printInfomationLevel>=1){
Print("★ トレール利益を超えた Bid= ",Bid," OrderOpenPrice()= ",OrderOpenPrice()," doTrailingOrderModify ticket= ",ticket," magicAllProfit= ",magicAllProfit);
}
// 現在の逆指値価格を計算
double newStopLoss = (OrderType() == OP_BUY) ? Bid - trailingProfitPips * pointPerPips : Ask + trailingProfitPips * pointPerPips;
// 既存の逆指値を更新
if ((OrderType() == OP_BUY && (OrderStopLoss() < newStopLoss || OrderStopLoss() == 0)) ||
(OrderType() == OP_SELL && (OrderStopLoss() > newStopLoss || OrderStopLoss() == 0)))
{
if (!OrderModify(ticket, 0, NormalizeDouble(newStopLoss, Digits), 0, 0, clrYellow))
// if (!OrderModify(ticket, 0, newStopLoss, 0, 0, clrYellow))
{
if (printInfomationLevel>=1){
Print("OrderModify failed: ", GetLastError(), " OrderTicket: ", OrderTicket(),"OrderMagicNumber= ", OrderMagicNumber());
}
}
else
{
if (printInfomationLevel>=1){
Print("OrderModify succeeded for ticket: ", OrderTicket(), " New StopLoss: ", DoubleToString(newStopLoss, Digits));
}
}
}
}
return(ticket);
}
//+------------------------------------------------------------------+
//
int doGetInfomation(){
buySum = 0; // Buyポジションの加重取得価格合計
sellSum = 0; // Sellポジションの加重取得価格合計
buyLotSum = 0; // Buyポジションのロット数合計
sellLotSum = 0; // Sellポジションのロット数合計
buyAverage = 0; // Buyポジションの平均取得価格
sellAverage = 0; // Sellポジションの平均取得価格
buyLossCutLine =0 ;
sellLossCutLine =0 ;
buyPositionCount = 0;
sellPositionCount =0;
buyLotProfit = 0; // Buyポジションの利益合計
sellLotProfit = 0; // Sellポジションの利益合計
// ポジションのサーチ
for (int i = 0; i < OrdersTotal(); i++)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) // すべての注文をチェック
{
// if (OrderSymbol() == symbol) // 現在の通貨ペアが一致する場合
for(int j=0; j < trapCount; j++){
int magic = magicNumber+j;
// 注文状態をチェック
int ticket = getOrderTicket(magic);
if (ticket == OrderTicket() )
{
double lotSize = OrderLots(); // ポジションのロット数を取得
double openPrice = OrderOpenPrice(); // ポジションの取得価格を取得
if (OrderType() == OP_BUY) // Buyポジションの場合
{
buySum += openPrice * lotSize; // ロット数で加重した取得価格を合計
buyLotSum += lotSize; // Buyポジションのロット数を合計
buyPositionCount++;
buyLotProfit += OrderProfit();
}
else if (OrderType() == OP_SELL) // Sellポジションの場合
{
sellSum += openPrice * lotSize; // ロット数で加重した取得価格を合計
sellLotSum += lotSize; // Sellポジションのロット数を合計
sellPositionCount++;
sellLotProfit += OrderProfit();
}
}
}
}
}
// Buyポジションの平均取得価格を計算
buyAverage = buyLotSum > 0 ? buySum / buyLotSum : 0;
// Sellポジションの平均取得価格を計算
sellAverage = sellLotSum > 0 ? sellSum / sellLotSum : 0;
buyLossCutLine = buyLotSum > 0
? Bid - (AccountFreeMargin() / (buyLotSum * 100))/digitMultiplier
: 0;
sellLossCutLine = sellLotSum > 0
? Bid + (AccountFreeMargin() / (sellLotSum * 100))/digitMultiplier
: 0;
buyAverage = NormalizeDouble(buyAverage,Digits);
sellAverage = NormalizeDouble(sellAverage,Digits);
buyLotSum=NormalizeDouble(buyLotSum,Digits);
sellLotSum=NormalizeDouble(sellLotSum,Digits);
buyLossCutLine= NormalizeDouble(buyLossCutLine,Digits);
sellLossCutLine=NormalizeDouble(sellLossCutLine,Digits);
buyLotProfit= NormalizeDouble(buyLotProfit,Digits);
sellLotProfit=NormalizeDouble(sellLotProfit,Digits);
if (printInfomationLevel>=2){
Print("Buy Average: ", buyAverage, ", Buy Lot Sum: ", buyLotSum);
Print("Sell Average: ", sellAverage, ", Sell Lot Sum: ", sellLotSum);
Print("buyPositionCount : ", buyPositionCount, ", sellPositionCount: ", sellPositionCount);
Print("Buy Loss Cut Line: ", buyLossCutLine, ", Sell Loss Cut Line: ", sellLossCutLine);
}
return(0);
}
//+------------------------------------------------------------------+
//利益計算ロジック
double getMagicProfit(){
int tmp=0;
magicAllProfit=0;
RefreshRates();
for(int i=OrdersTotal()-1; i>=0; i--)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) ==false) continue;
if(OrderType() > 1) continue;
if(OrderSymbol() != Symbol()) continue;
tmp=(OrderMagicNumber()/roundDown)*roundDown;
if (tmp != magicNumber)continue;
magicAllProfit=magicAllProfit+OrderProfit();
}
//magicAllProfit = NormalizeDouble(magicAllProfit, Digits);
return(true);
}
//+------------------------------------------------------------------+