2021-09-24 21:05:12 +08:00
import logging
import logging . config
from dataclasses import dataclass
import MetaTrader5 as mt5
import numpy as np
2021-09-22 23:27:08 +08:00
import pandas as pd
from sklearn . model_selection import train_test_split
2021-09-24 21:05:12 +08:00
2021-09-22 23:27:08 +08:00
import config
2021-09-24 21:05:12 +08:00
logging . config . fileConfig ( " logging.conf " )
logger = logging . getLogger ( " app " )
# if config.DEBUGMODE:
# LOGFORMAT='%(asctime)s %(message)s'
# logging.basicConfig(filename=config.LOGFILENAME, format=LOGFORMAT, encoding='utf-8', level=logging.DEBUG)
2021-09-22 23:27:08 +08:00
@dataclass
class Client :
account : int
password : str
server : str
pair : str
lot : float
timeframe : np . ndarray
def login ( self ) :
" Client login "
2021-09-24 21:05:12 +08:00
if mt5 . login (
login = self . account , password = self . password , server = self . server
) :
logger . info (
f " Logged in to { self . server } with account { self . account } "
)
2021-09-22 23:27:08 +08:00
return True
else :
2021-09-24 21:05:12 +08:00
logger . critical (
f " Failed to login to { self . server } with account { self . account } "
)
2021-09-22 23:27:08 +08:00
return False
2023-12-06 18:00:26 +08:00
def get_all_data ( self ) :
""" Get all data and save it as CSV file in local directory """
rates = mt5 . copy_rates_from_pos ( self . pair , self . timeframe , 0 , 9999 )
df = pd . DataFrame ( rates )
df . to_csv ( f " { self . pair } .csv " )
return rates
def analyze_and_trade ( self , data = None ) :
""" Analyze the past 100 rates and trade on the current open price.
@param data - number of rates to fetch from the past """
if data is None :
rates = self . get_all_data ( )
else :
rates = mt5 . copy_rates_from_pos ( self . pair , self . timeframe , 1 , data )
2021-09-24 21:05:12 +08:00
logger . debug ( f " Fetched rates for { self . pair } : \n { rates } " )
2021-09-22 23:27:08 +08:00
df_data = pd . DataFrame ( rates )
2023-12-06 18:00:26 +08:00
# From df_data, make a new column called the next close price. The next close price will show the maximum or minimum close price in the next 100 rates.
df_data [ " next_close_price " ] = (
df_data [ " close " ]
. rolling ( 100 )
. apply (
lambda x : max ( x )
if x [ 0 ] == x . max ( )
else min ( x )
if x [ 0 ] == x . min ( )
else None ,
raw = True ,
)
)
print ( df_data . tail ( ) )
2021-09-22 23:27:08 +08:00
df_x = df_data . drop ( columns = " close " )
df_y = df_data [ " close " ]
x_train , x_test , y_train , y_test = train_test_split (
df_x , df_y , test_size = 0.2 , random_state = 0
)
2021-09-24 21:05:12 +08:00
logger . debug ( f " x_train : \n { x_train } " )
logger . debug ( f " x_test : \n { x_test } " )
logger . debug ( f " y_train : \n { y_train } " )
logger . debug ( f " y_test : \n { y_test } " )
2021-09-22 23:27:08 +08:00
model = self . get_model_to_predict ( x_train , y_train )
2021-09-24 21:05:12 +08:00
logger . debug ( f " Trained Model: \n { model } " )
current_open_price , to_predict = self . get_current_rate_to_predict ( )
2021-09-22 23:27:08 +08:00
self . y_predict = model . predict ( to_predict )
if current_open_price > float ( self . y_predict [ 0 ] ) :
2021-09-24 21:05:12 +08:00
logger . debug (
f " Sell at { current_open_price } with predicted take profit { float ( self . y_predict [ 0 ] ) } "
)
2021-09-22 23:27:08 +08:00
self . order ( " sell " )
else :
2021-09-24 21:05:12 +08:00
logger . debug (
f " Buy at { current_open_price } with predicted take profit { float ( self . y_predict [ 0 ] ) } "
)
2021-09-22 23:27:08 +08:00
self . order ( " buy " )
def get_current_rate_to_predict ( self ) :
"""
2021-09-24 21:05:12 +08:00
Get the current rate , and open price
2021-09-22 23:27:08 +08:00
"""
2021-09-24 21:05:12 +08:00
current_rate = mt5 . copy_rates_from_pos (
self . pair , config . TIMEFRAME_TO_TRADE , 0 , 1
)
2021-09-22 23:27:08 +08:00
current_df = pd . DataFrame ( current_rate )
current_open_price = current_df . iloc [ 0 ] [ " open " ]
2021-09-24 21:05:12 +08:00
return current_open_price , current_df . drop ( columns = " close " )
2021-09-22 23:27:08 +08:00
def get_model_to_predict ( self , x_train , y_train ) :
"""
Build a model to fit with the x and y training data set and fit .
Returns the built and learned model
"""
from sklearn . linear_model import LinearRegression
return LinearRegression ( ) . fit ( x_train , y_train )
def order ( self , signal ) :
" Gets ASK price and place a ORDER_TYPE_BUY "
symbol_info = mt5 . symbol_info ( self . pair )
if symbol_info is None :
2021-09-24 21:05:12 +08:00
logger . error ( f " { self . pair } not found, cannot call order_check() " )
2021-09-22 23:27:08 +08:00
mt5 . shutdown ( )
quit ( )
if not symbol_info . visible :
2021-09-24 21:05:12 +08:00
logger . debug ( f " { self . pair } is not visible, trying to switch on " )
2021-09-22 23:27:08 +08:00
if not mt5 . symbol_select ( self . pair , True ) :
2021-09-24 21:05:12 +08:00
logger . error ( f " symbol_select( { self . pair } ) failed, exit " )
2021-09-22 23:27:08 +08:00
mt5 . shutdown ( )
quit ( )
2021-09-24 21:05:12 +08:00
logger . debug (
f " { self . pair } ' s Point: { mt5 . symbol_info ( self . pair ) . point } "
)
logger . debug (
f " { self . pair } ' s Ask Price: { mt5 . symbol_info_tick ( self . pair ) . ask } "
)
logger . debug (
f " { self . pair } ' s Ask Price with Point: { mt5 . symbol_info_tick ( self . pair ) . ask * mt5 . symbol_info ( self . pair ) . point } "
)
logger . debug (
f " { self . pair } ' s Bid Price: { mt5 . symbol_info_tick ( self . pair ) . bid } "
)
logger . debug (
f " { self . pair } ' s Bid Price with Point: { mt5 . symbol_info_tick ( self . pair ) . bid * mt5 . symbol_info ( self . pair ) . point } "
)
2021-09-22 23:27:08 +08:00
order_type , price = (
( mt5 . ORDER_TYPE_SELL , mt5 . symbol_info_tick ( self . pair ) . bid )
if signal == " sell "
else ( mt5 . ORDER_TYPE_BUY , mt5 . symbol_info_tick ( self . pair ) . ask )
)
self . raw_order (
action = mt5 . TRADE_ACTION_DEAL ,
symbol = self . pair ,
volume = self . lot ,
type = order_type ,
price = price ,
tp = float ( self . y_predict [ 0 ] ) ,
deviation = 20 ,
magic = 20210922 ,
comment = " my last shot " ,
2021-09-24 21:05:12 +08:00
type_time = mt5 . ORDER_TIME_DAY ,
type_filling = mt5 . ORDER_FILLING_IOC ,
2021-09-22 23:27:08 +08:00
)
2021-09-24 21:05:12 +08:00
logger . debug (
2021-09-22 23:27:08 +08:00
f " Order Sent : by { self . pair } { self . lot } lots at { price } with deviation 20 points "
)
def raw_order ( self , * * kwargs ) :
2021-09-24 21:05:12 +08:00
logger . debug ( kwargs )
2021-09-22 23:27:08 +08:00
result = mt5 . order_send ( kwargs )
if result . retcode != mt5 . TRADE_RETCODE_DONE :
2021-09-24 21:05:12 +08:00
logger . error ( f " Order Send Failed, RETCODE = { result . retcode } " )
2021-09-22 23:27:08 +08:00
else :
2021-09-24 21:05:12 +08:00
logger . debug ( f " Order send done. Result: { result } " )
2021-09-22 23:27:08 +08:00
return result
2021-09-24 21:05:12 +08:00
def check_existing_positions ( self ) :
"""
Returns if there ' s any existing positions.
This is to help keep a limit to the number of orders a specific pair can handle .
In this function , its to handle that there can only be 1 existing active order per symbol / pair .
"""
logger . debug ( f " Checking pair: { self . pair } " )
pos = mt5 . positions_get ( symbol = self . pair )
logger . debug ( f " { pos } " )
return len ( pos ) == 0
2021-09-22 23:27:08 +08:00
2021-09-24 21:05:12 +08:00
def main ( ) :
2021-09-22 23:27:08 +08:00
if not mt5 . initialize ( ) :
2021-09-24 21:05:12 +08:00
logger . critical ( f " MT5 Init failed, error code { mt5 . last_error ( ) } " )
2021-09-22 23:27:08 +08:00
quit ( )
else :
for p in config . PAIRS :
c = Client (
config . ACCOUNT ,
config . PASSWORD ,
config . SERVER ,
p ,
config . LOT_SIZE ,
config . TIMEFRAME ,
)
2021-09-24 21:05:12 +08:00
if c . login ( ) and c . check_existing_positions ( ) :
2023-12-06 18:00:26 +08:00
# c.analyze_and_trade()
c . get_all_data ( )
2021-09-24 21:05:12 +08:00
if __name__ == " __main__ " :
2023-12-06 18:00:26 +08:00
# while True:
# try:
# main()
# except (KeyboardInterrupt, SystemExit):
# logger.critical("Manually quit the program")
# raise
main ( )