1+ import django
2+ from django .db .models import Sum
3+ from datetime import datetime , timedelta
4+ from os import environ
5+ from pandas import DataFrame , concat
6+ environ ['DJANGO_SETTINGS_MODULE' ] = 'lndg.settings'
7+ django .setup ()
8+ from gui .models import Forwards , Channels , LocalSettings , FailedHTLCs
9+
10+ def main (channels ):
11+ channels_df = DataFrame .from_records (channels .values ())
12+ filter_1day = datetime .now () - timedelta (days = 1 )
13+ filter_7day = datetime .now () - timedelta (days = 7 )
14+ if channels_df .shape [0 ] > 0 :
15+ if LocalSettings .objects .filter (key = 'AF-MaxRate' ).exists ():
16+ max_rate = int (LocalSettings .objects .filter (key = 'AF-MaxRate' )[0 ].value )
17+ else :
18+ LocalSettings (key = 'AF-MaxRate' , value = '2500' ).save ()
19+ max_rate = 2500
20+ if LocalSettings .objects .filter (key = 'AF-MinRate' ).exists ():
21+ min_rate = int (LocalSettings .objects .filter (key = 'AF-MinRate' )[0 ].value )
22+ else :
23+ LocalSettings (key = 'AF-MinRate' , value = '0' ).save ()
24+ min_rate = 0
25+ if LocalSettings .objects .filter (key = 'AF-Increment' ).exists ():
26+ increment = int (LocalSettings .objects .filter (key = 'AF-Increment' )[0 ].value )
27+ else :
28+ LocalSettings (key = 'AF-Increment' , value = '5' ).save ()
29+ increment = 5
30+ if LocalSettings .objects .filter (key = 'AF-Multiplier' ).exists ():
31+ multiplier = int (LocalSettings .objects .filter (key = 'AF-Multiplier' )[0 ].value )
32+ else :
33+ LocalSettings (key = 'AF-Multiplier' , value = '5' ).save ()
34+ multiplier = 5
35+ if LocalSettings .objects .filter (key = 'AF-FailedHTLCs' ).exists ():
36+ failed_htlc_limit = int (LocalSettings .objects .filter (key = 'AF-FailedHTLCs' )[0 ].value )
37+ else :
38+ LocalSettings (key = 'AF-FailedHTLCs' , value = '25' ).save ()
39+ failed_htlc_limit = 25
40+ if LocalSettings .objects .filter (key = 'AF-UpdateHours' ).exists ():
41+ update_hours = int (LocalSettings .objects .filter (key = 'AF-UpdateHours' ).get ().value )
42+ else :
43+ LocalSettings (key = 'AF-UpdateHours' , value = '24' ).save ()
44+ update_hours = 24
45+ if LocalSettings .objects .filter (key = 'AF-LowLiqLimit' ).exists ():
46+ lowliq_limit = int (LocalSettings .objects .filter (key = 'AF-LowLiqLimit' ).get ().value )
47+ else :
48+ LocalSettings (key = 'AF-LowLiqLimit' , value = '5' ).save ()
49+ lowliq_limit = 15
50+ if LocalSettings .objects .filter (key = 'AF-ExcessLimit' ).exists ():
51+ excess_limit = int (LocalSettings .objects .filter (key = 'AF-ExcessLimit' ).get ().value )
52+ else :
53+ LocalSettings (key = 'AF-ExcessLimit' , value = '95' ).save ()
54+ excess_limit = 90
55+ if lowliq_limit >= excess_limit :
56+ print ('Invalid thresholds detected, using defaults...' )
57+ lowliq_limit = 5
58+ excess_limit = 95
59+ forwards = Forwards .objects .filter (forward_date__gte = filter_7day , amt_out_msat__gte = 1000000 )
60+ if forwards .exists ():
61+ forwards_df_in_7d_sum = DataFrame .from_records (forwards .values ('chan_id_in' ).annotate (amt_out_msat = Sum ('amt_out_msat' ), fee = Sum ('fee' )), 'chan_id_in' )
62+ forwards_df_out_7d_sum = DataFrame .from_records (forwards .values ('chan_id_out' ).annotate (amt_out_msat = Sum ('amt_out_msat' ), fee = Sum ('fee' )), 'chan_id_out' )
63+ else :
64+ forwards_df_in_7d_sum = DataFrame ()
65+ forwards_df_out_7d_sum = DataFrame ()
66+ channels_df ['amt_routed_in_7day' ] = channels_df .apply (lambda row : int (forwards_df_in_7d_sum .loc [row .chan_id ].amt_out_msat / 1000 ) if (forwards_df_in_7d_sum .index == row .chan_id ).any () else 0 , axis = 1 )
67+ channels_df ['amt_routed_out_7day' ] = channels_df .apply (lambda row : int (forwards_df_out_7d_sum .loc [row .chan_id ].amt_out_msat / 1000 ) if (forwards_df_out_7d_sum .index == row .chan_id ).any () else 0 , axis = 1 )
68+ channels_df ['net_routed_7day' ] = channels_df .apply (lambda row : round ((row ['amt_routed_out_7day' ]- row ['amt_routed_in_7day' ])/ row ['capacity' ], 1 ), axis = 1 )
69+ channels_df ['local_balance' ] = channels_df .apply (lambda row : row .local_balance + row .pending_outbound , axis = 1 )
70+ channels_df ['remote_balance' ] = channels_df .apply (lambda row : row .remote_balance + row .pending_inbound , axis = 1 )
71+ channels_df ['in_percent' ] = channels_df .apply (lambda row : int (round ((row ['remote_balance' ]/ row ['capacity' ])* 100 , 0 )), axis = 1 )
72+ channels_df ['out_percent' ] = channels_df .apply (lambda row : int (round ((row ['local_balance' ]/ row ['capacity' ])* 100 , 0 )), axis = 1 )
73+ channels_df ['eligible' ] = channels_df .apply (lambda row : (datetime .now ()- row ['fees_updated' ]).total_seconds () > (update_hours * 3600 ), axis = 1 )
74+
75+ # Low Liquidity
76+ lowliq_df = channels_df [channels_df ['out_percent' ] <= lowliq_limit ].copy ()
77+ failed_htlc_df = DataFrame .from_records (FailedHTLCs .objects .exclude (wire_failure = 99 ).filter (timestamp__gte = filter_1day ).order_by ('-id' ).values ())
78+ if failed_htlc_df .shape [0 ] > 0 :
79+ failed_htlc_df = failed_htlc_df [(failed_htlc_df ['wire_failure' ]== 15 ) & (failed_htlc_df ['failure_detail' ]== 6 ) & (failed_htlc_df ['amount' ]> failed_htlc_df ['chan_out_liq' ]+ failed_htlc_df ['chan_out_pending' ])]
80+ lowliq_df ['failed_out_1day' ] = 0 if failed_htlc_df .empty else lowliq_df .apply (lambda row : len (failed_htlc_df [failed_htlc_df ['chan_id_out' ]== row .chan_id ]), axis = 1 )
81+ # INCREASE IF (failed htlc > threshhold) && (flow in == 0)
82+ lowliq_df ['new_rate' ] = lowliq_df .apply (lambda row : row ['local_fee_rate' ]+ (5 * multiplier ) if row ['failed_out_1day' ]> failed_htlc_limit and row ['amt_routed_in_7day' ] == 0 else row ['local_fee_rate' ], axis = 1 )
83+
84+ # Balanced Liquidity
85+ balanced_df = channels_df [(channels_df ['out_percent' ] > lowliq_limit ) & (channels_df ['out_percent' ] < excess_limit )].copy ()
86+ # IF NO FLOW THEN DECREASE FEE AND IF HIGH FLOW THEN SLOWLY INCREASE FEE
87+ balanced_df ['new_rate' ] = balanced_df .apply (lambda row : row ['local_fee_rate' ]+ ((2 * multiplier )* (1 + (row ['net_routed_7day' ]/ row ['capacity' ]))) if row ['net_routed_7day' ] > row ['capacity' ] else row ['local_fee_rate' ], axis = 1 )
88+ balanced_df ['new_rate' ] = balanced_df .apply (lambda row : row ['local_fee_rate' ]- (3 * multiplier ) if (row ['amt_routed_in_7day' ]+ row ['amt_routed_out_7day' ]) == 0 else row ['local_fee_rate' ], axis = 1 )
89+
90+ # Excess Liquidity
91+ excess_df = channels_df [channels_df ['out_percent' ] >= excess_limit ].copy ()
92+ excess_df ['revenue_7day' ] = excess_df .apply (lambda row : int (forwards_df_out_7d_sum .loc [row .chan_id ].fee ) if forwards_df_out_7d_sum .empty == False and (forwards_df_out_7d_sum .index == row .chan_id ).any () else 0 , axis = 1 )
93+ excess_df ['revenue_assist_7day' ] = excess_df .apply (lambda row : int (forwards_df_in_7d_sum .loc [row .chan_id ].fee ) if forwards_df_in_7d_sum .empty == False and (forwards_df_in_7d_sum .index == row .chan_id ).any () else 0 , axis = 1 )
94+ # DECREASE IF (assisting channel or stagnant liq)
95+ excess_df ['new_rate' ] = excess_df .apply (lambda row : row ['local_fee_rate' ]- (5 * multiplier ) if row ['net_routed_7day' ] < 0 and row ['revenue_assist_7day' ] > (row ['revenue_7day' ]* 10 ) else row ['local_fee_rate' ], axis = 1 )
96+ excess_df ['new_rate' ] = excess_df .apply (lambda row : row ['local_fee_rate' ]- (5 * multiplier ) if (row ['amt_routed_in_7day' ]+ row ['amt_routed_out_7day' ]) == 0 else row ['local_fee_rate' ], axis = 1 )
97+
98+ #Merge back results
99+ result_df = concat ([lowliq_df , balanced_df , excess_df ])
100+ result_df ['new_rate' ] = result_df .apply (lambda row : int (round (row ['new_rate' ]/ increment , 0 )* increment ), axis = 1 )
101+ result_df ['new_rate' ] = result_df .apply (lambda row : max_rate if max_rate < row ['new_rate' ] else row ['new_rate' ], axis = 1 )
102+ result_df ['new_rate' ] = result_df .apply (lambda row : min_rate if min_rate > row ['new_rate' ] else row ['new_rate' ], axis = 1 )
103+ result_df ['adjustment' ] = result_df .apply (lambda row : int (row ['new_rate' ]- row ['local_fee_rate' ]), axis = 1 )
104+ return result_df
105+ else :
106+ return DataFrame ()
107+
108+
109+ if __name__ == '__main__' :
110+ print (main (Channels .objects .filter (is_open = True )))
0 commit comments