Package: pysailfish
pysailfishExample EA --- simple clientfrompysailfish.internal.MT_EA.MT4_EAimportMT4_EAclassMT4DemoEA(MT4_EA):def__init__(self):super().__init__()# overridedef_OnInit(self)->int:self._logger.info(self._user_inputs)self._logger.info("Here")return0# overridedef_OnDeinit(self,reason:int)->None:self._logger.info(f"Here reason:{reason}")returnNone# overridedef_OnTick(self)->None:self._logger.info("Here")returnNone# overridedef_OnTimer(self)->None:returnNone# overridedef_OnTester(self)->float:self._logger.info("Here")return0.0# overridedef_OnChartEvent(self,id:int,lparam:int,dparam:float,sparam:str)->None:self._logger.info(f"Here id:{id}lparam:{lparam}dparam:{dparam}sparam:{sparam}")returnNonedefmain()->None:ea=MT4DemoEA()ea.InitComponent(server_ip="127.0.0.1",server_port=23456,ea_name="MT4DemoEA")ea.StartEA()if__name__=="__main__":main()Example EA --- Moving Average EAfromdatetimeimportdatetimeimportpysailfish.internal.MT_EA.mt4_constasmcfrompysailfish.internal.MT_EA.MT4_EAimportMT4_EAclassMA_EA(MT4_EA):def__init__(self):super().__init__()# overridedef_OnInit(self)->int:self._logger.info(self._user_inputs)self._logger.info("Here")return0# overridedef_OnDeinit(self,reason:int)->None:self._logger.info(f"Here reason:{reason}")returnNone# overridedef_OnTick(self)->None:vv=self.iADX(symbol=self._pv.symbol,timeframe=mc.PERIOD_CURRENT,period=self._user_inputs["MovingPeriod"],applied_price=mc.PRICE_CLOSE,mode=mc.MODE_MAIN,shift=0)# self._logger.info(f"Here {self._pv.symbol} {self._pv.time[0]}")# --- check for history and tradingifself._pv.bars<100orself._pv.is_trade_allowed==False:return# --- calculate open orders by current symbolifself.__calculate_current_orders(self._pv.symbol)==0:self.__check_for_open()else:self.__check_for_close()returnNone# overridedef_OnTimer(self)->None:returnNone# overridedef_OnTester(self)->float:self._logger.info("Here")return0.0# overridedef_OnChartEvent(self,id:int,lparam:int,dparam:float,sparam:str)->None:self._logger.info(f"Here id:{id}lparam:{lparam}dparam:{dparam}sparam:{sparam}")returnNonedef__check_for_close(self)->None:ma:float=0#--- go trading only for first tiks of new barifself._pv.volume[0]>1:returnNone#--- get Moving Averagema=self.iMA(symbol=self._pv.symbol,timeframe=mc.PERIOD_CURRENT,ma_period=self._user_inputs["MovingPeriod"],ma_shift=self._user_inputs["MovingShift"],ma_method=mc.MODE_SMA,applied_price=mc.PRICE_CLOSE,shift=0)#---foriinrange(self.OrdersTotal()):ifself.OrderSelect(index=i,select=mc.SELECT_BY_POS,pool=mc.MODE_TRADES)==False:breakifself.OrderMagicNumber()!=self._pv.magic_numorself.OrderSymbol()!=self._pv.symbol:continue#--- check order typeifself.OrderType()==mc.OP_BUY:ifself._pv.open[1]>maandself._pv.close[1]<ma:ifnotself.OrderClose(ticket=self.OrderTicket(),lots=self.OrderLots(),price=self._pv.bid,slippage=3,arrow_color=mc.clrWhite):self._logger.error(f"OrderClose error{self.GetLastError()}")breakifself.OrderType()==mc.OP_SELL:ifself._pv.open[1]<maandself._pv.close[1]>ma:ifnotself.OrderClose(ticket=self.OrderTicket(),lots=self.OrderLots(),price=self._pv.ask,slippage=3,arrow_color=mc.clrWhite):self._logger.error(f"OrderClose error{self.GetLastError()}");breakdef__lots_optimized(self)->float:lot:float=float(self._user_inputs["Lots"])maximum_risk:float=float(self._user_inputs["MaximumRisk"])decrease_factor:float=float(self._user_inputs["DecreaseFactor"])orders:int=self.OrdersHistoryTotal()# history orders totallosses:int=0# number of losses orders without a break#--- select lot sizelot=self.NormalizeDouble(value=(self.AccountFreeMargin()*maximum_risk/1000.0),digits=1);#--- calcuulate number of losses orders without a breakifdecrease_factor>0:foriinreversed(range(orders)):ifself.OrderSelect(index=i,select=mc.SELECT_BY_POS,pool=mc.MODE_HISTORY)==False:self._logger.error("Error in history")breakifself.OrderSymbol()!=self._pv.symbolorself.OrderType()>mc.OP_SELL:continue#---ifself.OrderProfit()>0:breakifself.OrderProfit()<0:losses+=1iflosses>1:lot=self.NormalizeDouble(value=(lot-lot*losses/decrease_factor),digits=1)#--- return lot sizeiflot<0.1:lot=0.1returnlotdef__check_for_open(self)->None:ma:float=0res:int=0#--- go trading only for first tiks of new barifself._pv.volume[0]>1:returnNone#--- get Moving Averagema=self.iMA(symbol=self._pv.symbol,timeframe=mc.PERIOD_CURRENT,ma_period=self._user_inputs["MovingPeriod"],ma_shift=self._user_inputs["MovingShift"],ma_method=mc.MODE_SMA,applied_price=mc.PRICE_CLOSE,shift=0)#--- sell conditionsifself._pv.open[1]>maandself._pv.close[1]<ma:res=self.OrderSend(symbol=self._pv.symbol,cmd=mc.OP_SELL,volume=self.__lots_optimized(),price=self._pv.bid,slippage=3,stoploss=0,takeprofit=0,comment="",magic=self._pv.magic_num,expiration=datetime(1970,1,1,0,0,0),arrow_color=mc.clrRed)returnNone#--- buy conditionsifself._pv.open[1]<maandself._pv.close[1]>ma:res=self.OrderSend(symbol=self._pv.symbol,cmd=mc.OP_BUY,volume=self.__lots_optimized(),price=self._pv.ask,slippage=3,stoploss=0,takeprofit=0,comment="",magic=self._pv.magic_num,expiration=datetime(1970,1,1,0,0,0),arrow_color=mc.clrBlue)returnNonedef__calculate_current_orders(self,symbol:str)->int:buys:int=0sells:int=0# ---foriinrange(self.OrdersTotal()):ifself._tf.order_select(index=i,select=mc.SELECT_BY_POS,pool=mc.MODE_TRADES)==False:breakifself.OrderSymbol()==self._pv.symbolandself.OrderMagicNumber()==self._pv.magic_num:ifself.OrderType()==mc.OP_BUY:buys+=1ifself.OrderType()==mc.OP_SELL:sells+=1# --- return orders volumeifbuys>0:returnbuyselse:return-sellsdefmain()->None:ea=MA_EA()ea.InitComponent(server_ip="127.0.0.1",server_port=23456,ea_name="MA_EA")ea.StartEA()if__name__=="__main__":main()How to publish to pypi# set up pypi token$poetryconfigpypi-token.pypimy-token# build the project$poetrybuild# publish the project$poetrypublish# DONE