pip shaker

今回は、PipShakerOne_2019のコードを説明しながら、売買ロジック・アルゴリズムを解説します。
PipShakerOne_2019は過去記事【PipShakerOne】でダウンロードできます。

過去記事【PipShakerOne】へ

PipShakerOne_2019の解説

Dr.EADr.EA

PipShakerOne_2019のコードを見てみるぞい!

#property version   "1.00"
#property strict

enum t_type {Buy, Sell};

extern string     _txt_00_             = "";    // --- 全般設定 ---
extern double     Slippage             = 1.0;
extern string     PosComment           = "PipShaker";
extern int        MagicNumber          = 777;
extern double     TrailingPips         = 5.0;
extern double     StopLoss             = 200;
extern double     ProfitTarget         = 30.0;
extern double     Spacing              = 5.0;

extern string     _txt_10_             = "";    // --- 第一取引設定 ---
extern datetime   Datetime_1stEntry    = D'2019.01.01 09:00';
extern t_type     TradeType_1stEntry   = Buy;

extern string     _txt_20_             = "";    // --- ロット設定 ---
extern double     MaxLotSize           = 1.0;
extern double     LotSize              = 0.01;
extern double     LotIncrement         = 0.01;
extern double     MaxTotalLots         = 6.0;

extern string     _txt_30_             = "";    // --- 移動平均線設定 ---
extern ENUM_TIMEFRAMES TrendTimeFrame  = PERIOD_CURRENT;
extern int        TrendPeriod          = 200;

int entry_cnt = 0;

double g_point;    // 1pipの値
int g_lot_digit;    // ロット小数桁数

int OnInit()
{
   g_point = Point;
   if (Digits % 2 == 1)
   {
      g_point *= 10;
      Slippage *= 10;
   }
   
   double lotstep = MarketInfo(Symbol(), MODE_LOTSTEP);
   if(NormalizeDouble(lotstep, 3) <= 0.001)
      g_lot_digit = 3;
   else if(NormalizeDouble(lotstep, 2) <= 0.01)
      g_lot_digit = 2;
   else if(NormalizeDouble(lotstep, 1) <= 0.1)
      g_lot_digit = 1;
   else
      g_lot_digit = 0;
      
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
}

void OnTick()
{
   int i;
   double sl, ts;
   
   // Check Positions ::::::::::::::::::::::::::::::::::::::::
   
   double   lowest_buy = 0;
   double   highest_buy = 0;
   double   lowest_sell = 0;
   double   highest_sell = 0;
   
   int      highest_ticket_buy = 0;
   int      lowest_ticket_buy = 0;
   int      highest_ticket_sell = 0;
   int      lowest_ticket_sell = 0;
   
   double   highest_profit_buy = 0;
   double   lowest_profit_buy = 0;
   double   highest_profit_sell = 0;
   double   lowest_profit_sell = 0;
   
   int      pos_cnt_buy = 0;
   int      pos_cnt_sell = 0;
   
   double   total_lots_buy = 0;
   double   total_lots_sell = 0;
   
   double   plus_profit_buy = 0;
   double   plus_profit_sell = 0;
   
   for (i=0; i<OrdersTotal(); i++)
   {
      if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == false) return;
      if (OrderSymbol() != Symbol() || OrderMagicNumber() != MagicNumber) continue;
      
      double profit = OrderProfit() + OrderSwap() + OrderCommission();
      
      if (OrderType() == OP_BUY)
      {
         if (highest_buy == 0 || OrderOpenPrice() > highest_buy)
         {
            highest_buy = OrderOpenPrice();
            highest_ticket_buy = OrderTicket();
            highest_profit_buy = profit;
         }

         if (lowest_buy == 0 || OrderOpenPrice() < lowest_buy)
         {
            lowest_buy = OrderOpenPrice();
            lowest_ticket_buy = OrderTicket();
            lowest_profit_buy = profit;
         }
         
         pos_cnt_buy++;
         total_lots_buy += OrderLots();
         if (profit > 0.0) plus_profit_buy += profit;
      }
      if (OrderType() == OP_SELL)
      {
         if (highest_sell == 0 || OrderOpenPrice() > highest_sell)
         {
            highest_sell = OrderOpenPrice();
            highest_ticket_sell = OrderTicket();
            highest_profit_sell = profit;
         }

         if (lowest_sell == 0 || OrderOpenPrice() < lowest_sell)
         {
            lowest_sell = OrderOpenPrice();
            lowest_ticket_sell = OrderTicket();
            lowest_profit_sell = profit;
         }

         pos_cnt_sell++;
         total_lots_sell += OrderLots();
         if (profit > 0.0) plus_profit_sell += profit;
      }
   }
   
   // exit  :::::::::::::::::::::::::::::::::::::::::::::::::::
   
   // 買いポジションのみ保有
   if (pos_cnt_sell == 0 && pos_cnt_buy > 1)
   {
      if (plus_profit_buy - MathMax(0, highest_profit_buy) >= ProfitTarget)
      {
         CloseWin(highest_ticket_buy);
         return;
      }
   }
   
   // 売りポジションのみ保有
   if (pos_cnt_buy == 0 && pos_cnt_sell > 1)
   {
      if (plus_profit_sell - MathMax(0, lowest_profit_sell) >= ProfitTarget)
      {
         CloseWin(lowest_ticket_sell);
         return;
      }
   }
   
   // mid point 算出
   double high_point = MathMax(highest_buy, highest_sell);
   double low_point = lowest_buy;
   if (lowest_sell > 0 && (low_point == 0 || low_point > lowest_sell)) low_point = lowest_sell;
   double mid_point = (high_point + low_point) * 0.5;
   
   if (Ask > mid_point)
      if (lowest_profit_sell < 0)
         if (plus_profit_buy + plus_profit_sell + lowest_profit_sell >= ProfitTarget)
            if (OrderSelect(lowest_ticket_sell, SELECT_BY_TICKET) == true)
               if (OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), (int)Slippage, clrYellow) == true) CloseWin();
               
   if (Bid < mid_point)
      if (highest_profit_buy < 0)
         if (plus_profit_buy + plus_profit_sell + highest_profit_buy >= ProfitTarget)
            if (OrderSelect(highest_ticket_buy, SELECT_BY_TICKET) == true)
               if (OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), (int)Slippage, clrYellow) == true) CloseWin();
   
   // Trailing
   if (TrailingPips > 0 && pos_cnt_buy + pos_cnt_sell == 1)
   {
      for (i=0; i<OrdersTotal(); i++)
      {
         if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == false) continue;
         if (OrderSymbol() != Symbol() || OrderMagicNumber() != MagicNumber) continue;
         
         sl = NormalizeDouble(OrderStopLoss(), Digits);
         
         if (OrderType() == OP_BUY)
         {
            ts = NormalizeDouble(Bid - TrailingPips * g_point, Digits);
            if (ts >= OrderOpenPrice() && (sl == 0 || sl < ts))
               if( OrderModify(OrderTicket(), OrderOpenPrice(), ts, OrderTakeProfit(), 0, clrRed) == false) Print("OrderModify error.");
         }
         if (OrderType() == OP_SELL)
         {
            ts = NormalizeDouble(Ask + TrailingPips * g_point, Digits);
            if (ts <= OrderOpenPrice() && (sl == 0 || sl > ts))
               if( OrderModify(OrderTicket(), OrderOpenPrice(), ts, OrderTakeProfit(), 0, clrRed) == false) Print("OrderModify error.");
         }
      }
   }
   
   
   // Check entry signal ::::::::::::::::::::::::::::::::::::::::
   
   int sign = 0;
   
   // 1st entry signal
   if (pos_cnt_buy + pos_cnt_sell == 0 && TimeCurrent() >= Datetime_1stEntry && entry_cnt == 0)
   {
      if (TradeType_1stEntry == Buy) sign = 1;
      if (TradeType_1stEntry == Sell) sign = -1;
   }
   
   // add pos signal
   if (pos_cnt_buy + pos_cnt_sell > 0)
   {
      double ma_0 = iMA(Symbol(), TrendTimeFrame, TrendPeriod, 0, MODE_LWMA, PRICE_CLOSE, 0);
      double ma_1 = iMA(Symbol(), TrendTimeFrame, TrendPeriod, 0, MODE_LWMA, PRICE_CLOSE, 1);
      
      if (ma_1 < ma_0)
      {
         if (entry_cnt == 1 && pos_cnt_sell > 0)
         {
            highest_buy = highest_sell + (Ask-Bid);
            lowest_buy = highest_sell + (Ask-Bid);
         }
         
         if (Ask < lowest_buy - Spacing * g_point || Ask > highest_buy + Spacing * g_point) sign = 1;
      }
      if (ma_1 > ma_0)
      {
         if (entry_cnt == 1 && pos_cnt_buy > 0)
         {
            highest_sell = highest_buy - (Ask-Bid);
            lowest_sell = highest_buy - (Ask-Bid);
         }
         
         if (Bid < lowest_sell - Spacing * g_point || Bid > highest_sell + Spacing * g_point) sign = -1;
      }
      
      // 買いポジ1つの場合 上昇時エントリーしない
      if (pos_cnt_buy == 1 && pos_cnt_sell == 0 && Ask > highest_buy) sign = 0;
      // 売りポジ1つの場合 下落時エントリーしない
      if (pos_cnt_buy == 0 && pos_cnt_sell == 1 && Bid < lowest_sell) sign = 0;
   }
   
   
   // entry  :::::::::::::::::::::::::::::::::::::::::::::::::::
   
   // Buy Entry
   if (sign == 1)
   {
      sl = 0;
      if (StopLoss > 0) sl = Ask - StopLoss * g_point;
      
      double lots = NormalizeDouble(LotSize + LotIncrement * pos_cnt_buy, g_lot_digit);
      if (lots == 0.0) lots = NormalizeDouble(LotSize, g_lot_digit);
      if (lots > MaxLotSize) lots = MaxLotSize;
      if (total_lots_buy + lots <= MaxTotalLots)
         if (OrderSend(Symbol(), OP_BUY, lots, Ask, (int)Slippage, sl, 0, PosComment, MagicNumber, 0, clrBlue) > 0) entry_cnt++;
   }
   
   // Sell Entry
   if (sign == -1)
   {
      sl = 0;
      if (StopLoss > 0) sl = Bid + StopLoss * g_point;
      
      double lots = NormalizeDouble(LotSize + LotIncrement * pos_cnt_sell, g_lot_digit);
      if (lots == 0.0) lots = NormalizeDouble(LotSize, g_lot_digit);
      if (lots > MaxLotSize) lots = MaxLotSize;
      if (total_lots_sell + lots <= MaxTotalLots)
         if (OrderSend(Symbol(), OP_SELL, lots, Bid, (int)Slippage, sl, 0, PosComment, MagicNumber, 0, clrRed) > 0) entry_cnt++;
   }
}

void CloseWin(int exclude_ticket = 0)
{
   RefreshRates();
   for (int i=OrdersTotal()-1; i>=0; i--)
   {
      if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == false) continue;
      if (OrderSymbol() != Symbol() || OrderMagicNumber() != MagicNumber) continue;
      if (OrderTicket() == exclude_ticket) continue;
      
      if (OrderProfit() + OrderSwap() + OrderCommission() > 0.0)
         if (OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), (int)Slippage, clrYellow) == false) Print("OrderClose error.");
   }
}
にゃんたにゃんた

ほぇ~・・・

パラメーター

次の表にあるパラメーターを設置しています。

— 全般設定 —
Slippage 許容スリッページ(単位:Pips)
PosComment ポジションコメント
MagicNumber マジックナンバー
TrailingPips トレーリングストップ(単位:Pips)
StopLoss SL値(単位:Pips)
ProfitTarget 目標利益(単位は口座の通貨)
Spacing エントリー間隔(単位:Pips)
— 第一取引設定 —
Datetime_1stEntry 最初のエントリーの日時
TradeType_1stEntry 最初のエントリーの売買指定(Buy/Sell)
— ロット設定 —
MaxLotSize 最大ロット数
LotSize 最初のエントリーのロット数
LotIncrement 買い増し売り増し時のロット増加数
MaxTotalLots 売買別 ロット数合計の上限
— 移動平均線設定 —
TrendTimeFrame 移動平均の時間足
TrendPeriod 移動平均の期間

グローバル変数

グローバル変数には、エントリー回数・1pipの値・ロット数小数以下桁数を格納する変数を宣言しています。

int entry_cnt = 0;
double g_point;    // 1pipの値
int g_lot_digit;    // ロット小数桁数

OnInit()関数内の処理

OnInit()関数内で、グローバル変数g_pointとg_lot_digitにそれぞれ値を格納しています。

   g_point = Point;
   if (Digits % 2 == 1)
   {
      g_point *= 10;
      Slippage *= 10;
   }
   
   double lotstep = MarketInfo(Symbol(), MODE_LOTSTEP);
   if(NormalizeDouble(lotstep, 3) <= 0.001)
      g_lot_digit = 3;
   else if(NormalizeDouble(lotstep, 2) <= 0.01)
      g_lot_digit = 2;
   else if(NormalizeDouble(lotstep, 1) <= 0.1)
      g_lot_digit = 1;
   else
      g_lot_digit = 0;

保有ポジション状況確認

OnTick()関数内の最初に、保有ポジション状況を確認して買いポジションと売りポジションそれぞれの次の情報を取得しています。

  • 取得価格の最高値・最安値
  • 取得価格の最高値・最安値の注文番号
  • 取得価格の最高値・最安値の損益額
  • ポジション数
  • 合計ロット数
  • プラス損益の利益合計

片側ポジションのみ保有時の決済処理

買いポジションのみ保有または売りポジションのみ保有の場合の決済を次のようにしています。CloseWin()関数に注文番号を渡すとそのポジションは決済対象から除外されるようにしています。

   // 買いポジションのみ保有
   if (pos_cnt_sell == 0 && pos_cnt_buy > 1)
   {
      if (plus_profit_buy - MathMax(0, highest_profit_buy) >= ProfitTarget)
      {
         CloseWin(highest_ticket_buy);
         return;
      }
   }
   
   // 売りポジションのみ保有
   if (pos_cnt_buy == 0 && pos_cnt_sell > 1)
   {
      if (plus_profit_sell - MathMax(0, lowest_profit_sell) >= ProfitTarget)
      {
         CloseWin(lowest_ticket_sell);
         return;
      }
   }

ここで「plus_profit_buy – MathMax(0, highest_profit_buy)」は、highest_profit_buyがプラス損益の場合にのみplus_profit_buyからその利益分を差し引く計算になります。

取得価格の中心を求める

売買区別なく、エントリー価格の最高値と最安値の中心を次のように求めています。

   double high_point = MathMax(highest_buy, highest_sell);
   double low_point = lowest_buy;
   if (lowest_sell > 0 && (low_point == 0 || low_point > lowest_sell)) low_point = lowest_sell;
   double mid_point = (high_point + low_point) * 0.5;

相殺決済処理

現在値から一番遠くにあるマイナス損益のポジションを、プラス損益のポジション群で相殺決済をするようにしています。

   if (Ask > mid_point)
      if (lowest_profit_sell < 0)
         if (plus_profit_buy + plus_profit_sell + lowest_profit_sell >= ProfitTarget)
            if (OrderSelect(lowest_ticket_sell, SELECT_BY_TICKET) == true)
               if (OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), (int)Slippage, clrYellow) == true) CloseWin();
               
   if (Bid < mid_point)
      if (highest_profit_buy < 0)
         if (plus_profit_buy + plus_profit_sell + highest_profit_buy >= ProfitTarget)
            if (OrderSelect(highest_ticket_buy, SELECT_BY_TICKET) == true)
               if (OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), (int)Slippage, clrYellow) == true) CloseWin();

トレーリングストップ処理

保有ポジションが1つの場合にトレーリングストップ機能が発動するようにしています。

   if (TrailingPips > 0 && pos_cnt_buy + pos_cnt_sell == 1)
   {
      // 中略
   }

第一エントリーのシグナル判定

第一エントリーのシグナル判定を次のようにしています。PipShakerOneは、第一エントリーを一度だけにしています。

   if (pos_cnt_buy + pos_cnt_sell == 0 && TimeCurrent() >= Datetime_1stEntry && entry_cnt == 0)
   {
      if (TradeType_1stEntry == Buy) sign = 1;
      if (TradeType_1stEntry == Sell) sign = -1;
   }

追加エントリーのシグナル判定

ポジション保有時の追加エントリーのシグナルを次のようにMAの方向とSpacingで判定しています。

   if (pos_cnt_buy + pos_cnt_sell > 0)
   {
      double ma_0 = iMA(Symbol(), TrendTimeFrame, TrendPeriod, 0, MODE_LWMA, PRICE_CLOSE, 0);
      double ma_1 = iMA(Symbol(), TrendTimeFrame, TrendPeriod, 0, MODE_LWMA, PRICE_CLOSE, 1);
      
      if (ma_1 < ma_0)
      {
         if (entry_cnt == 1 && pos_cnt_sell > 0)
         {
            highest_buy = highest_sell + (Ask-Bid);
            lowest_buy = highest_sell + (Ask-Bid);
         }
         
         if (Ask < lowest_buy - Spacing * g_point || Ask > highest_buy + Spacing * g_point) sign = 1;
      }
      if (ma_1 > ma_0)
      {
         if (entry_cnt == 1 && pos_cnt_buy > 0)
         {
            highest_sell = highest_buy - (Ask-Bid);
            lowest_sell = highest_buy - (Ask-Bid);
         }
         
         if (Bid < lowest_sell - Spacing * g_point || Bid > highest_sell + Spacing * g_point) sign = -1;
      }
      
      if (pos_cnt_buy == 1 && pos_cnt_sell == 0 && Ask > highest_buy) sign = 0;
      if (pos_cnt_buy == 0 && pos_cnt_sell == 1 && Bid < lowest_sell) sign = 0;
   }

エントリー処理

signが1の時に買い注文、-1の時に売り注文をするようにしています。注文が通ったらentry_cntに1を足しています。

   if (sign == 1)
   {
      // 買いエントリー処理 中略
         if (OrderSend(Symbol(), OP_BUY, lots, Ask, (int)Slippage, sl, 0, PosComment, MagicNumber, 0, clrBlue) > 0) entry_cnt++;
   }

   if (sign == -1)
   {
      // 売りエントリー処理 中略
         if (OrderSend(Symbol(), OP_SELL, lots, Bid, (int)Slippage, sl, 0, PosComment, MagicNumber, 0, clrRed) > 0) entry_cnt++;
   }

あとがき

サラッと解説しただけなので、分からないところがあったらコメントで聞いてください^^;

相殺決済処理などいろいろとカスタマイズすると面白そうですね^^
では、このへんで。