Showing 11 changed files with 743 additions and 0 deletions
+33
bids.py
... ...
@@ -0,0 +1,33 @@
1
+#!/usr/bin/env python3
2
+
3
+import time
4
+import datetime
5
+
6
+import bot
7
+import queries
8
+
9
+from binance.exceptions import BinanceAPIException
10
+
11
+def cancel_current_best_offer(best_offer_price_, best_bid_price_):
12
+    print(f'\n| ###{datetime.datetime.now().strftime("%H:%M")}###')
13
+    print(f'| BID at price {best_bid_price_} was EXECUTED;')
14
+
15
+    try:
16
+        bot.CLIENT.cancel_order(symbol=bot.TRADING_PAIR, orderId=queries.get_order_id(best_offer_price_))  # CANCEL currentlly 'BEST OFFER'
17
+        print(f'| Cancelling BEST OFFER: {best_offer_price_}')
18
+    except BinanceAPIException:
19
+        print('[!!!] Error while canceling BEST OFFER')
20
+
21
+# A function for replacing the executed BID
22
+def update_rel_to_best_bid(best_offer_price_, best_bid_price_):
23
+    if not queries.order_active(best_offer_price_):  # If NEW best OFFER order is not already or yet present on the market
24
+        print(f'| Placing NEW BEST OFFER at lower price: {best_offer_price_}')
25
+        bot.CLIENT.order_limit_sell(symbol=bot.TRADING_PAIR, quantity=bot.COINS_PER_ORDER, price=best_offer_price_)  # Placing an OFFER (NEW best OFFER) at a WINDOW price
26
+        time.sleep(1)
27
+    
28
+    if not queries.order_active(best_bid_price_):  # If NEW best BID order is not already or yet present on the market
29
+        print(f'| Placing NEW BEST BID at lower price: {best_bid_price_}')
30
+        bot.CLIENT.order_limit_buy(symbol=bot.TRADING_PAIR, quantity=bot.COINS_PER_ORDER, price=best_bid_price_)  # Replacing executed BID with NEW best BID
31
+        time.sleep(1)
32
+
33
+    print('| ########### \n')
+165
bot.py
... ...
@@ -0,0 +1,165 @@
1
+#!/usr/bin/env python3
2
+
3
+# pip3 install -r requirements.txt
4
+
5
+import time
6
+import datetime
7
+import json
8
+import sys
9
+
10
+import bids
11
+import offers
12
+import reports
13
+import calc
14
+import launch
15
+import queries
16
+
17
+from binance.client import Client
18
+
19
+#========== Reading CONFIG file ==========#
20
+with open('config.json', 'r') as config_file:  # Open and read the 'config.json' file
21
+    config_data = json.load(config_file)
22
+    
23
+    #---------- Binance_bot data ----------#
24
+    API_KEY = config_data['Binance_bot']['api_key']
25
+    API_SECRET = config_data['Binance_bot']['api_secret']
26
+
27
+    TRADING_ASSET = config_data['Binance_bot']['asset']  # Coin to trade against of (ex. BTC)
28
+    TRADING_COIN = config_data['Binance_bot']['coin']  # Trading coin (ex. BNB)
29
+    TRADING_PAIR = str(TRADING_COIN + TRADING_ASSET)  # Trading pair: BNB for BTC => BNBBTC
30
+
31
+    SPREAD = int(config_data['Binance_bot']['spread'])  # Distance between: BID => OFFER in number of STEPS (see line below)
32
+    SPREAD_TO_PRINT = config_data['Binance_bot']['spread']
33
+    
34
+    STEP = float(config_data['Binance_bot']['step'])  # Distance between: order(of any type) => order(of any type) in asset value
35
+    STEP_TO_PRINT = config_data['Binance_bot']['step']
36
+
37
+    COINS_PER_ORDER = float(config_data['Binance_bot']['coins_per_order'])  # Quantity of coins for one BID
38
+    COINS_PER_ORDER_TO_PRINT = config_data['Binance_bot']['coins_per_order']
39
+    
40
+    #---------- Email_sender data ----------#
41
+    SENDER_ADDRESS = config_data['Email_sender']['sender_address']
42
+    SENDER_PASS = config_data['Email_sender']['sender_pass']
43
+    
44
+    RECEIVER_ADDRESS = config_data['Email_sender']['receiver_address']
45
+
46
+#========== Connecting to API ==========#
47
+CLIENT = Client(API_KEY, API_SECRET)  # Connecting to the Binance API
48
+
49
+#========== Setting Up Constants ==========#
50
+INIT_COIN_PRICE = queries.get_coin_price()  #  An initial coin price
51
+NUM_DECIMAL_PLACES = calc.count_decimal_places()  # Round all prices to the constant number of decimal places
52
+
53
+PRICES_ARR = calc.generate_grid_of_prices()  # An array of initial prices
54
+
55
+#========== If ran as main ==========#
56
+if __name__ == "__main__":
57
+
58
+    #========== Setting UP ==========#
59
+    current_day = datetime.datetime.now().strftime("%d")  # Current day of month
60
+    current_week = datetime.datetime.now().strftime("%W")  # Current day of the week
61
+    current_month = datetime.datetime.now().strftime("%b")  # Current month
62
+
63
+    tmp_day = current_day  # Temporal variable for holding current day of month
64
+    tmp_week = current_week  # Temporal variable for holding current day of the week
65
+    tmp_month = current_month  # Temporal variable for holding current month
66
+
67
+    asset_balance = CLIENT.get_asset_balance(asset=TRADING_ASSET)  # An array of info about the original balance
68
+    asset_balance_free = float(asset_balance['free'])  # Original balance of free asset
69
+    coin_balace = CLIENT.get_asset_balance(asset=TRADING_COIN)     #  An array of info about the original coin balance
70
+    coin_balance_free = float(coin_balace['free'])  # Original balance of free coin
71
+
72
+    window_price = calc.find_current_window_price()  # An empty window for BID/OFFER orders
73
+    best_offer_price = calc.find_best_offer_price(window_price) # Current best OFFER
74
+    best_bid_price = calc.find_best_bid_price(best_offer_price)  # Current best BID
75
+        
76
+    #========== Printing INFO ==========#
77
+    launch.start_trading_ui(asset_balance_free, coin_balance_free)
78
+    
79
+    print('\n| ----------')
80
+    print(f'| Best OFFER: {best_offer_price}')
81
+    print(f'| WINDOW: {window_price}')
82
+    print(f'| Best BID: {best_bid_price}')
83
+    print('| ----------\n')
84
+
85
+    #========== Placing Initial Orders ==========#
86
+    launch.place_init_orders(best_offer_price, window_price, best_bid_price)
87
+
88
+    #========== Trading Loop ==========#
89
+    while True:  # The MAIN trading loop
90
+
91
+        try:
92
+            time.sleep(3)  # Each 10 seconds send requests to the market
93
+
94
+            current_day = datetime.datetime.now().strftime("%d")  # Update the current day of month
95
+            current_week = datetime.datetime.now().strftime("%W")  # Update the current day of the week
96
+            current_month = datetime.datetime.now().strftime("%b")  # Update the current month
97
+
98
+            if (not queries.order_active(best_offer_price) and queries.get_order_status(best_offer_price) == 'FILLED' 
99
+                and queries.enough_balance(asset_balance_free, coin_balance_free)):  # If the current best-deal OFFER was executed and there is enough balance to continue
100
+                
101
+                offers.cancel_current_best_bid(best_offer_price, best_bid_price)  # Cancel current best BID
102
+                
103
+                window_price = calc.up_current_window_price(window_price)  # New empty window for BID/OFFER orders
104
+                best_offer_price = calc.find_best_offer_price(window_price) # New best OFFER
105
+                best_bid_price = calc.find_best_bid_price(best_offer_price)  # Current best BID
106
+
107
+                offers.update_rel_to_best_offer(best_offer_price, best_bid_price)
108
+                reports.save_order("SELL", best_offer_price)
109
+
110
+                print('\n| ----------')
111
+                print(f'| Best OFFER: {best_offer_price}')
112
+                print(f'| WINDOW: {window_price}')
113
+                print(f'| Best BID: {best_bid_price}')
114
+                print('| ----------\n')
115
+
116
+            if (not queries.order_active(best_bid_price) and queries.get_order_status(best_bid_price) == 'FILLED' 
117
+                and queries.enough_balance(asset_balance_free, coin_balance_free)):  # If the current best-deal BID was executed and there is enough balance to continue
118
+                
119
+                bids.cancel_current_best_offer(best_offer_price, best_bid_price)    # Cancel current best OFFER
120
+                
121
+                window_price = calc.down_current_window_price(window_price)  # New empty window for BID/OFFER orders
122
+                best_offer_price = calc.find_best_offer_price(window_price) # New best OFFER
123
+                best_bid_price = calc.find_best_bid_price(best_offer_price)  # Current best BID
124
+
125
+                bids.update_rel_to_best_bid(best_offer_price, best_bid_price)
126
+                reports.save_order("BUY", best_bid_price)
127
+
128
+                print('\n| ----------')
129
+                print(f'| Best OFFER: {best_offer_price}')
130
+                print(f'| WINDOW: {window_price}')
131
+                print(f'| Best BID: {best_bid_price}')
132
+                print('| ----------\n')
133
+
134
+            if current_day != tmp_day:  # If day has passed
135
+                
136
+                #reports.daily_report(queries.num_closed_inday_offers(), calc.calc_asset_profit(queries.num_closed_inday_offers()))
137
+                tmp_day = current_day
138
+            
139
+                #print('\n| Daily Report Generated\n')
140
+            
141
+            if current_week != tmp_week:  # If week has passed
142
+                
143
+                reports.weekly_report(queries.num_closed_inweek_offers(), calc.calc_asset_profit(queries.num_closed_inweek_offers()))
144
+                tmp_week = current_week
145
+
146
+                print('\n| Weekly Report Generated\n')
147
+            
148
+            if current_month != tmp_month:  # If month has passed
149
+
150
+                #reports.monthly_report(queries.num_closed_inmonth_offers(), calc.calc_asset_profit(queries.num_closed_inmonth_offers()))
151
+                tmp_month = current_month
152
+            
153
+                #print('\n| Monthly Report Generated\n')
154
+
155
+            else:
156
+                continue
157
+
158
+        except KeyboardInterrupt:
159
+            sys.exit()
160
+
161
+        except Exception as e:
162
+            print(f'[!!!] [{datetime.datetime.now().strftime("%H:%M")}] {e}')
163
+            with open('errors.log', 'a') as errors_log:
164
+                errors_log.write(f'[{datetime.datetime.now().strftime("%d %b %Y; %H:%M")}] {e}\n')
165
+            continue
+56
calc.py
... ...
@@ -0,0 +1,56 @@
1
+#!/usr/bin/env python3
2
+
3
+import bot
4
+
5
+# A function to generate the grid of prices for future orders
6
+def generate_grid_of_prices():    
7
+    up_to_price = bot.INIT_COIN_PRICE * 10;  # Generate array up to this price 
8
+    grid_arr = []
9
+    order_price = 0  # A starting price
10
+    
11
+    while order_price < up_to_price:  # While the generated price is still smaller than the maximum possible price
12
+        grid_arr.append(round(order_price, bot.NUM_DECIMAL_PLACES)) # Append a new price to the array of prices
13
+        order_price = round(order_price + bot.STEP, bot.NUM_DECIMAL_PLACES)  # Add the STEP value to the future price
14
+    
15
+    return grid_arr
16
+
17
+# A function for finding the current WINDOW price
18
+def find_current_window_price():
19
+    for price in bot.PRICES_ARR: # Iterate through the array of prices
20
+        if price >= bot.INIT_COIN_PRICE: #  If the the price is larger than the current price
21
+            return price
22
+            break;
23
+        else:
24
+            continue;
25
+    
26
+    return None;
27
+
28
+def find_best_offer_price(window_price_):
29
+    window_price_i = bot.PRICES_ARR.index(window_price_)  # Index value of a window price
30
+    
31
+    if bot.SPREAD % 2 == 0:  # If SPREAD is even
32
+        return bot.PRICES_ARR[window_price_i + int(bot.SPREAD / 2)]  # Return the value whose index is half SPREAD higher than the WINDOW price
33
+    else:  # If SPREAD is odd
34
+        return bot.PRICES_ARR[window_price_i + (int(bot.SPREAD / 2) + 1)]  # Return the value whose index is one SPREAD higher than the WINDOW price
35
+    
36
+def find_best_bid_price(offer_price):
37
+    offer_price_i = bot.PRICES_ARR.index(offer_price)  # Index value of a window price
38
+    
39
+    return bot.PRICES_ARR[offer_price_i - (bot.SPREAD + 1)]  # Return the value whose index is SPREAD + 1 lower than the OFFER price
40
+    
41
+def up_current_window_price(window_price_):
42
+    return bot.PRICES_ARR[bot.PRICES_ARR.index(window_price_) + 1]  # + 1 STEP (adding 1 to index, but the next value is 1 STEP larger)
43
+    
44
+def down_current_window_price(window_price_):
45
+    return bot.PRICES_ARR[bot.PRICES_ARR.index(window_price_) - 1]  # - 1 STEP (subtracting 1 from index, but the previous value is 1 STEP lower)
46
+    
47
+def count_decimal_places():
48
+    init_coin_price_str = str(bot.INIT_COIN_PRICE)
49
+
50
+    return int(init_coin_price_str[::-1].find('.'))  # Count all simbols until meeting a '.'
51
+
52
+# A function for calculating the profit
53
+def calc_asset_profit(num_closed_spreads):
54
+    asset_profit = float(round(num_closed_spreads * (bot.SPREAD * bot.STEP * bot.COINS_PER_ORDER), bot.NUM_DECIMAL_PLACES))
55
+
56
+    return asset_profit
+16
config.json
... ...
@@ -0,0 +1,16 @@
1
+{
2
+    "Binance_bot": {
3
+        "api_key":"",
4
+        "api_secret":"",
5
+        "asset":"",
6
+        "coin":"",
7
+        "spread":"",
8
+        "step":"",
9
+        "coins_per_order":""
10
+    },
11
+    "Email_sender": {
12
+        "sender_address":"",
13
+        "sender_pass":"",
14
+        "receiver_address":""
15
+    }
16
+}
errors.log
No changes.
+59
launch.py
... ...
@@ -0,0 +1,59 @@
1
+#!/usr/bin/env python3
2
+
3
+import datetime
4
+import sys
5
+
6
+import bot
7
+import queries
8
+
9
+# A function for turning the float into a printable and understandable form
10
+# (e.g 2.22e-05 to '0.0000222')
11
+def _float_to_printable(num):
12
+    num_str = str(num)
13
+    
14
+    # If the given float is of the form x.xxx...xxxe-xxx
15
+    if 'e' in num_str:
16
+        num_of_zeros = int(num_str[len(num_str) - 1]) - 1 # Number of zeros after comma
17
+        e_index = num_str.index('e')
18
+        decimals = num_str[0] + num_str[2:e_index]  # Numbers after zeros (e.g all xxx in 0.000...0xxx)
19
+        printable_float = '0.' + '0' * num_of_zeros + decimals  # Combine the string float
20
+        
21
+        return printable_float
22
+    else:
23
+        return num_str
24
+        
25
+# A function for displaying the 'start_trading' initial interface
26
+def start_trading_ui(asset_balance_free_, coin_balance_free_):
27
+
28
+    print('\n**************************')
29
+    print('Starting trading session:')
30
+    print('--------------------------')
31
+    print(f'Time: {datetime.datetime.now().strftime("%H:%M")}')
32
+    print('--------------------------')
33
+    print(f'Step: {bot.STEP_TO_PRINT}  {bot.TRADING_ASSET.lower()}')
34
+    print(f'Spread: {bot.SPREAD_TO_PRINT} ({_float_to_printable(round(bot.SPREAD * bot.STEP, bot.NUM_DECIMAL_PLACES))}) {bot.TRADING_ASSET.lower()}')
35
+    print(f'{bot.TRADING_COIN} per BID: {bot.COINS_PER_ORDER_TO_PRINT}')
36
+    print('**************************\n')
37
+
38
+    while True:
39
+        start = input('Start trading(y/n): ')
40
+        if start.lower() == 'y':
41
+            break
42
+        elif start.lower() == 'n':
43
+            sys.exit()
44
+        else:
45
+            continue
46
+
47
+    if not queries.enough_balance(asset_balance_free_, coin_balance_free_):  # If there is not enough balance to start trading
48
+        print('\n| Not enough balance in asset or coin!')
49
+        sys.exit()
50
+
51
+# A fuction for placing the best BID and OFFER orders on the market
52
+def place_init_orders(best_offer_price_, window_price_, best_bid_price_):
53
+    if not queries.order_active(best_offer_price_):  # If the 'BEST OFFER' order is not already or yet present on the market
54
+        print(f'| Placing BEST OFFER at: {best_offer_price_}')
55
+        bot.CLIENT.order_limit_sell(symbol=bot.TRADING_PAIR, quantity=bot.COINS_PER_ORDER, price=best_offer_price_)
56
+
57
+    if not queries.order_active(best_bid_price_):  # If the 'BEST BID' order is not already or yet present on the market
58
+        print(f'| Placing BEST BID at: {best_bid_price_}')
59
+        bot.CLIENT.order_limit_buy(symbol=bot.TRADING_PAIR, quantity=bot.COINS_PER_ORDER, price=best_bid_price_)
+33
offers.py
... ...
@@ -0,0 +1,33 @@
1
+#!/usr/bin/env python3
2
+
3
+import time
4
+import datetime
5
+
6
+import bot
7
+import queries
8
+
9
+from binance.exceptions import BinanceAPIException
10
+
11
+def cancel_current_best_bid(best_offer_price_, best_bid_price_):
12
+    print(f'\n| @@@{datetime.datetime.now().strftime("%H:%M")}@@@')
13
+    print(f'| OFFER at price {best_offer_price_} was EXECUTED;')
14
+
15
+    try:
16
+        bot.CLIENT.cancel_order(symbol=bot.TRADING_PAIR, orderId=queries.get_order_id(best_bid_price_))  # CANCEL currentlly 'BEST BID'
17
+        print(f'| Cancelling BEST BID: {best_bid_price_}')
18
+    except BinanceAPIException:
19
+        print('[!!!] Error while canceling BEST BID')
20
+
21
+# A function for replacing the executed OFFER
22
+def update_rel_to_best_offer(best_offer_price_, best_bid_price_):
23
+    if not queries.order_active(best_offer_price_):  # If the NEW best OFFER order is not already or yet present on the market
24
+        print(f'| Placing NEW BEST OFFER at higher price: {best_offer_price_}')
25
+        bot.CLIENT.order_limit_sell(symbol=bot.TRADING_PAIR, quantity=bot.COINS_PER_ORDER, price=best_offer_price_)  # Replacing executed OFFER with NEW best OFFER
26
+        time.sleep(1)
27
+        
28
+    if not queries.order_active(best_bid_price_):  # If the NEW best BID order is not already or yet present on the market
29
+        print(f'| Placing NEW BEST BID at higher price: {best_bid_price_}')
30
+        bot.CLIENT.order_limit_buy(symbol=bot.TRADING_PAIR, quantity=bot.COINS_PER_ORDER, price=best_bid_price_)  # Placing a BID (NEW best BID) at a WINDOW price
31
+        time.sleep(1)
32
+
33
+    print('| @@@@@@@@@@@\n')
+119
queries.py
... ...
@@ -0,0 +1,119 @@
1
+#!/usr/bin/env python3
2
+
3
+import datetime
4
+
5
+import bot
6
+
7
+# A function to get the price of a certain coin
8
+def get_coin_price():
9
+    current_coin_price = float(bot.CLIENT.get_ticker(symbol=bot.TRADING_PAIR)['askPrice'])
10
+    
11
+    return current_coin_price
12
+
13
+# A function to get the id of the order at a certain price
14
+def get_order_id(coin_price):
15
+    orders_arr = bot.CLIENT.get_open_orders(symbol=bot.TRADING_PAIR)  # An array of active orders for the currentlly trading coin
16
+    
17
+    for order in orders_arr:
18
+        if round(float(order['price']), bot.NUM_DECIMAL_PLACES) == coin_price:
19
+            return order['orderId']  # Returns the order id
20
+        else:
21
+            continue
22
+
23
+# A function to get the status of the order at a certain price            
24
+def get_order_status(coin_price):
25
+    orders_arr = bot.CLIENT.get_all_orders(symbol=bot.TRADING_PAIR, limit=2)
26
+    
27
+    for order in orders_arr:
28
+        if round(float(order['price']), bot.NUM_DECIMAL_PLACES) == coin_price:
29
+            return order['status']
30
+        else:
31
+            continue
32
+    else:
33
+        return None
34
+
35
+# A function to check whether an order at a certain price is present on the market
36
+def order_active(coin_price):
37
+    orders_arr = bot.CLIENT.get_open_orders(symbol=bot.TRADING_PAIR)  # An array of active orders for the currentlly trading coin
38
+
39
+    for order in orders_arr:
40
+        if round(float(order['price']), bot.NUM_DECIMAL_PLACES) == coin_price:
41
+            return True
42
+    else:
43
+        return False
44
+
45
+# A function to check if there is enough balance in both, asset and coin for one more order
46
+def enough_balance(asset_balance_free_, coin_balance_free_): 
47
+    if (asset_balance_free_ >= bot.COINS_PER_ORDER * get_coin_price() and 
48
+        coin_balance_free_ >= bot.COINS_PER_ORDER):
49
+        
50
+        return True
51
+    else:
52
+        return False
53
+        
54
+# A function to convert the server-time to the datetime object
55
+def timestamp_to_date(timestamp):
56
+    global CLIENT
57
+
58
+    return datetime.datetime.fromtimestamp(float(timestamp)/1000)
59
+     
60
+# A function to count the IDs of orders, closed within last 24h
61
+def num_closed_inday_offers():
62
+    server_time = timestamp_to_date(bot.CLIENT.get_server_time()['serverTime'])  # 'To' date
63
+    from_date = server_time - datetime.timedelta(days=1)  # 'From' date
64
+
65
+    orders_arr = bot.CLIENT.get_all_orders(symbol=bot.TRADING_PAIR, limit=500)
66
+    decimal_places = len(str(bot.COINS_PER_ORDER).split('.')[1])
67
+    
68
+    count = 0
69
+    for order in orders_arr:
70
+        if (order['status'] == 'FILLED' and order['side'] == 'SELL' and round(float(order['origQty']), decimal_places) == bot.COINS_PER_ORDER
71
+            and (from_date < timestamp_to_date(order['updateTime']) < server_time)):
72
+
73
+            count += 1
74
+    
75
+    return count
76
+
77
+
78
+# A function to count the IDs of orders, closed within last week
79
+def num_closed_inweek_offers():
80
+    server_time = timestamp_to_date(bot.CLIENT.get_server_time()['serverTime'])  # 'To' date
81
+    from_date = server_time - datetime.timedelta(days=7)  # 'From' date
82
+
83
+    orders_arr = bot.CLIENT.get_all_orders(symbol=bot.TRADING_PAIR, limit=500)
84
+    decimal_places = len(str(bot.COINS_PER_ORDER).split('.')[1])
85
+    
86
+    count = 0
87
+    for order in orders_arr:
88
+        if (order['status'] == 'FILLED' and order['side'] == 'SELL' and round(float(order['origQty']), decimal_places) == bot.COINS_PER_ORDER
89
+            and (from_date < timestamp_to_date(order['updateTime']) < server_time)):
90
+
91
+            count += 1
92
+    
93
+    return count
94
+    
95
+# A function for getting the number of the previous month (ex. dec=12 => return nov=11)        
96
+def _get_past_month_num():
97
+    current_month_num = int(datetime.datetime.now().strftime("%m"))  # Get the current number of a month (1 - 12)
98
+    
99
+    if current_month_num == 1:
100
+        return 12
101
+    else:
102
+        return current_month_num - 1
103
+
104
+# A function to count the IDs of orders, closed within last month
105
+def num_closed_inmonth_offers():
106
+    server_time = timestamp_to_date(bot.CLIENT.get_server_time()['serverTime'])  # 'To' date
107
+    month_before_date = server_time.replace(month=_get_past_month_num())  # Date a month before
108
+
109
+    orders_arr = bot.CLIENT.get_all_orders(symbol=bot.TRADING_PAIR, limit=500)
110
+    decimal_places = len(str(bot.COINS_PER_ORDER).split('.')[1])
111
+
112
+    count = 0
113
+    for order in orders_arr:
114
+        if (order['status'] == 'FILLED' and order['side'] == 'SELL' and round(float(order['origQty']), decimal_places) == bot.COINS_PER_ORDER
115
+            and (month_before_date < timestamp_to_date(order['updateTime']) < server_time)):
116
+        
117
+            count += 1
118
+    
119
+    return count
+129
reports.py
... ...
@@ -0,0 +1,129 @@
1
+#!/usr/bin/env python3
2
+
3
+import datetime
4
+import csv
5
+
6
+import bot
7
+import send
8
+
9
+# A function for constructing the daily report in a '.csv' file
10
+def daily_report(num_closed_spreads, asset_profit):
11
+    current_date = datetime.datetime.now()  # Current date
12
+    day_before_date = current_date - datetime.timedelta(days=1)  # Date a day before
13
+    
14
+    day_before_date_str = day_before_date.strftime("%d.%m.%Y")
15
+    
16
+    report_name = day_before_date_str + '_daily.csv'
17
+    report_path = 'reports/daily/' + report_name
18
+    
19
+    with open(report_path, 'w') as database:
20
+            
21
+        headers =['date', 'closed', 'profit']
22
+        csv_writer = csv.DictWriter(database, headers)
23
+
24
+        csv_writer.writeheader()
25
+            
26
+        csv_writer.writerow({'date':day_before_date_str, 'closed':str(num_closed_spreads), 'profit':str(asset_profit)})
27
+    
28
+    try:  # Try to send the generated report via email
29
+        send.send_daily_report(report_name, day_before_date_str)
30
+        print('\n| Daily Report has been Sent\n')
31
+        
32
+    except FileNotFoundError:
33
+        print('[!!!] Cannot send the email: Report does not exists')
34
+        
35
+    except:
36
+        print('[!!!] Cannot send the email: Unexpected Error')
37
+
38
+# A function for constructing the weekly report in a '.csv' file
39
+def weekly_report(num_closed_spreads, asset_profit):
40
+    current_date = datetime.datetime.now()  # Current date
41
+    week_before_date = current_date - datetime.timedelta(days=7)  # Date a week before
42
+
43
+    current_date_str = current_date.strftime("%d.%m.%Y")
44
+    week_before_date_str = week_before_date.strftime("%d.%m.%Y")
45
+    
46
+    report_name = week_before_date_str + '_' + current_date_str + '_weekly.csv'
47
+    report_path = 'reports/weekly/' + report_name
48
+
49
+    with open(report_path, 'w') as database:
50
+            
51
+        headers =['date', 'closed', 'profit']
52
+        csv_writer = csv.DictWriter(database, headers)
53
+
54
+        csv_writer.writeheader()
55
+            
56
+        csv_writer.writerow({'date':current_date_str, 'closed':str(num_closed_spreads), 'profit':str(asset_profit)})
57
+        
58
+    try:  # Try to send the generated report via email
59
+        send.send_weekly_report(report_name, week_before_date_str + '-' + current_date_str)
60
+        print('\n| Weekly Report has been Sent\n')
61
+        
62
+    except FileNotFoundError:
63
+        print('[!!!] Cannot send the email: Report does not exists')
64
+        
65
+    except:
66
+        print('[!!!] Cannot send the email: Unexpected Error')
67
+
68
+# A function for getting the number of the previous month (ex. dec=12 => return nov=11)        
69
+def _get_past_month_num():
70
+    current_month_num = int(datetime.datetime.now().strftime("%m"))  # Get the current number of a month (1 - 12)
71
+    
72
+    if current_month_num == 1:
73
+        return 12
74
+    else:
75
+        return current_month_num - 1
76
+
77
+# A function for constructing the monthly report in a '.csv' file
78
+def monthly_report(num_closed_spreads, asset_profit):
79
+    current_date = datetime.datetime.now()  # Current date
80
+    month_before_date = current_date.replace(month=_get_past_month_num())  # Date a month before
81
+    
82
+    current_date_str = current_date.strftime("%d.%m.%Y")
83
+    month_before_date_str = month_before_date.strftime("%b.%Y")
84
+    
85
+    report_name = month_before_date_str + '_monthly.csv'
86
+    report_path = 'reports/monthly/' + report_name
87
+
88
+    with open(report_path, 'w') as database:
89
+            
90
+        headers =['date', 'closed', 'profit']
91
+        csv_writer = csv.DictWriter(database, headers)
92
+
93
+        csv_writer.writeheader()
94
+            
95
+        csv_writer.writerow({'date': current_date_str, 'closed':str(num_closed_spreads), 'profit':str(asset_profit)})
96
+        
97
+    try:  # Try to send the generated report via email
98
+        send.send_monthly_report(report_name, month_before_date_str)
99
+        print('\n| Monthly Report has been Sent\n')
100
+        
101
+    except FileNotFoundError:
102
+        print('[!!!] Cannot send the email: Report does not exists')
103
+        
104
+    except:
105
+        print('[!!!] Cannot send the email: Unexpected Error')
106
+
107
+# A function for saving all closed BIDS and OFFERS
108
+def save_order(type_, coin_price):
109
+    current_date_str = datetime.datetime.now().strftime("%d.%m.%Y")  # Current date
110
+    current_month_str = datetime.datetime.now().strftime("%b")  # Current month
111
+    current_year_str = datetime.datetime.now().strftime("%Y")  # Current year
112
+
113
+    try:  # Try to create a new file, if it does not exists and write down the info
114
+        with open('orders/' + current_month_str + '_' + current_year_str + '_orders.csv', 'x') as database:
115
+            
116
+            headers =['date', 'pair', 'type', 'price', 'coins']
117
+            csv_writer = csv.DictWriter(database, headers)
118
+
119
+            csv_writer.writeheader()
120
+    except FileExistsError:
121
+        pass
122
+    
123
+    # However if the file already exists, then open it in 'append' mode
124
+    with open('orders/' + current_month_str + '_' + current_year_str + '_orders.csv', 'a') as database:
125
+            
126
+        headers =['date', 'pair', 'type', 'price', 'coins']
127
+        csv_writer = csv.DictWriter(database, headers)
128
+             
129
+        csv_writer.writerow({'date': current_date_str, 'pair': bot.TRADING_PAIR, 'type': type_, 'price': str(coin_price), 'coins': bot.COINS_PER_ORDER_TO_PRINT})
+10
requirements.txt
... ...
@@ -0,0 +1,10 @@
1
+python-binance==0.7.2
2
+requests==2.22.0
3
+autobahn==18.7.1
4
+certifi==2018.4.16
5
+chardet==3.0.4
6
+cryptography==2.3
7
+dateparser==0.7.0
8
+pyOpenSSL==19.0.0
9
+service-identity==17.0.0
10
+Twisted==18.7.0
+123
send.py
... ...
@@ -0,0 +1,123 @@
1
+#!/usr/bin/env python3
2
+
3
+import smtplib
4
+from email.mime.multipart import MIMEMultipart
5
+from email.mime.text import MIMEText
6
+from email.mime.base import MIMEBase
7
+from email import encoders
8
+
9
+import bot
10
+
11
+# https://www.google.com/settings/security/lesssecureapps
12
+# https://accounts.google.com/DisplayUnlockCaptcha
13
+
14
+def send_daily_report(report_name_, date_):
15
+    mail_content = '''
16
+    Hello,
17
+    
18
+    This is a Daily Report email.
19
+    Find the report document attached.
20
+    
21
+    Best Regards,
22
+    Your Bot
23
+    '''
24
+
25
+    message = MIMEMultipart() # instance of MIMEMultipart 
26
+    message['From'] = bot.SENDER_ADDRESS
27
+    message['To'] = bot.RECEIVER_ADDRESS
28
+    message['Subject'] = 'Binance bot; ' + bot.TRADING_PAIR + ' (Daily Report: ' + date_ + ')'
29
+      
30
+    message.attach(MIMEText(mail_content, 'plain'))  # attach the body with the msg instance 
31
+    
32
+    attachment = open('reports/daily/' + report_name_, "rb") # open the file to be sent 
33
+      
34
+    payload = MIMEBase('application', 'octet-stream') # instance of MIMEBase and named as 'payload'
35
+    payload.set_payload((attachment).read())  # To change the payload into encoded form 
36
+      
37
+    encoders.encode_base64(payload)  # encode into base64 
38
+
39
+    payload.add_header('Content-Disposition', "attachment; filename= %s" % report_name_)  # attach the instance 'payload' to instance 'message' 
40
+    message.attach(payload) 
41
+      
42
+    session = smtplib.SMTP('smtp.gmail.com', 587)  # creates SMTP session 
43
+    session.starttls()  # start TLS for security  
44
+    session.login(bot.SENDER_ADDRESS, bot.SENDER_PASS)  # Authentication
45
+
46
+    text = message.as_string()  # Converts the Multipart msg into a string 
47
+
48
+    session.sendmail(bot.SENDER_ADDRESS, bot.RECEIVER_ADDRESS, text)  # sending the mail
49
+    session.quit() # terminating the session
50
+    
51
+def send_weekly_report(report_name_, date_):
52
+    mail_content = '''
53
+    Hello,
54
+    
55
+    This is a Weekly Report email.
56
+    Find the report document attached.
57
+    
58
+    Best Regards,
59
+    Your Bot
60
+    '''
61
+
62
+    message = MIMEMultipart() # instance of MIMEMultipart 
63
+    message['From'] = bot.SENDER_ADDRESS
64
+    message['To'] = bot.RECEIVER_ADDRESS
65
+    message['Subject'] = 'Binance bot; ' + bot.TRADING_PAIR + ' (Weekly Report: ' + date_ + ')'
66
+      
67
+    message.attach(MIMEText(mail_content, 'plain'))  # attach the body with the msg instance 
68
+    
69
+    attachment = open('reports/weekly/' + report_name_, "rb") # open the file to be sent 
70
+      
71
+    payload = MIMEBase('application', 'octet-stream') # instance of MIMEBase and named as 'payload'
72
+    payload.set_payload((attachment).read())  # To change the payload into encoded form 
73
+      
74
+    encoders.encode_base64(payload)  # encode into base64 
75
+
76
+    payload.add_header('Content-Disposition', "attachment; filename= %s" % report_name_)  # attach the instance 'payload' to instance 'message' 
77
+    message.attach(payload) 
78
+      
79
+    session = smtplib.SMTP('smtp.gmail.com', 587)  # creates SMTP session 
80
+    session.starttls()  # start TLS for security  
81
+    session.login(bot.SENDER_ADDRESS, bot.SENDER_PASS)  # Authentication
82
+
83
+    text = message.as_string()  # Converts the Multipart msg into a string 
84
+
85
+    session.sendmail(bot.SENDER_ADDRESS, bot.RECEIVER_ADDRESS, text)  # sending the mail
86
+    session.quit() # terminating the session
87
+    
88
+def send_monthly_report(report_name_, date_):
89
+    mail_content = '''
90
+    Hello,
91
+    
92
+    This is a Monthly Report email.
93
+    Find the report document attached.
94
+    
95
+    Best Regards,
96
+    Your Bot
97
+    '''
98
+
99
+    message = MIMEMultipart() # instance of MIMEMultipart 
100
+    message['From'] = bot.SENDER_ADDRESS
101
+    message['To'] = bot.RECEIVER_ADDRESS
102
+    message['Subject'] = 'Binance bot; ' + bot.TRADING_PAIR + ' (Monthly Report: ' + date_ + ')'
103
+      
104
+    message.attach(MIMEText(mail_content, 'plain'))  # attach the body with the msg instance 
105
+    
106
+    attachment = open('reports/monthly/' + report_name_, "rb") # open the file to be sent 
107
+      
108
+    payload = MIMEBase('application', 'octet-stream') # instance of MIMEBase and named as 'payload'
109
+    payload.set_payload((attachment).read())  # To change the payload into encoded form 
110
+      
111
+    encoders.encode_base64(payload)  # encode into base64 
112
+
113
+    payload.add_header('Content-Disposition', "attachment; filename= %s" % report_name_)  # attach the instance 'payload' to instance 'message' 
114
+    message.attach(payload) 
115
+      
116
+    session = smtplib.SMTP('smtp.gmail.com', 587)  # creates SMTP session 
117
+    session.starttls()  # start TLS for security  
118
+    session.login(bot.SENDER_ADDRESS, bot.SENDER_PASS)  # Authentication
119
+
120
+    text = message.as_string()  # Converts the Multipart msg into a string 
121
+
122
+    session.sendmail(bot.SENDER_ADDRESS, bot.RECEIVER_ADDRESS, text)  # sending the mail
123
+    session.quit() # terminating the session